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(Constants.RESOURCE_PARTITION_ID, RequestPartitionId.defaultPartition());
269                        }
270                }
271        }
272
273        @SuppressWarnings("unchecked")
274        private <R extends IBaseResource> R parseResource(
275                        IBaseResourceEntity<?> theEntity,
276                        ResourceEncodingEnum theResourceEncoding,
277                        String theDecodedResourceText,
278                        Class<R> theResourceType) {
279                R retVal;
280                if (theResourceEncoding == ResourceEncodingEnum.ESR) {
281
282                        int colonIndex = theDecodedResourceText.indexOf(':');
283                        Validate.isTrue(colonIndex > 0, "Invalid ESR address: %s", theDecodedResourceText);
284                        String providerId = theDecodedResourceText.substring(0, colonIndex);
285                        String address = theDecodedResourceText.substring(colonIndex + 1);
286                        Validate.notBlank(providerId, "No provider ID in ESR address: %s", theDecodedResourceText);
287                        Validate.notBlank(address, "No address in ESR address: %s", theDecodedResourceText);
288                        IExternallyStoredResourceService provider =
289                                        myExternallyStoredResourceServiceRegistry.getProvider(providerId);
290                        retVal = (R) provider.fetchResource(address);
291
292                } else if (theResourceEncoding != ResourceEncodingEnum.DEL) {
293
294                        IParser parser = new TolerantJsonParser(
295                                        getContext(theEntity.getFhirVersion()), LENIENT_ERROR_HANDLER, theEntity.getResourceId());
296
297                        try {
298                                retVal = parser.parseResource(theResourceType, theDecodedResourceText);
299                        } catch (Exception e) {
300                                StringBuilder b = new StringBuilder();
301                                b.append("Failed to parse database resource[");
302                                b.append(myFhirContext.getResourceType(theResourceType));
303                                b.append("/");
304                                b.append(theEntity.getIdDt().getIdPart());
305                                b.append(" (pid ");
306                                b.append(theEntity.getId());
307                                b.append(", version ");
308                                b.append(theEntity.getFhirVersion().name());
309                                b.append("): ");
310                                b.append(e.getMessage());
311                                String msg = b.toString();
312                                ourLog.error(msg, e);
313                                throw new DataFormatException(Msg.code(928) + msg, e);
314                        }
315
316                } else {
317
318                        retVal = (R) myFhirContext
319                                        .getResourceDefinition(theEntity.getResourceType())
320                                        .newInstance();
321                }
322                return retVal;
323        }
324
325        @SuppressWarnings("unchecked")
326        private <R extends IBaseResource> Class<R> determineTypeToParse(
327                        Class<R> theResourceType, @Nullable Collection<? extends BaseTag> tagList) {
328                Class<R> resourceType = theResourceType;
329                if (tagList != null) {
330                        if (myFhirContext.hasDefaultTypeForProfile()) {
331                                for (BaseTag nextTag : tagList) {
332                                        if (nextTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
333                                                String profile = nextTag.getTag().getCode();
334                                                if (isNotBlank(profile)) {
335                                                        Class<? extends IBaseResource> newType = myFhirContext.getDefaultTypeForProfile(profile);
336                                                        if (newType != null && theResourceType.isAssignableFrom(newType)) {
337                                                                ourLog.debug("Using custom type {} for profile: {}", newType.getName(), profile);
338                                                                resourceType = (Class<R>) newType;
339                                                                break;
340                                                        }
341                                                }
342                                        }
343                                }
344                        }
345                }
346                return resourceType;
347        }
348
349        @SuppressWarnings("unchecked")
350        @Override
351        public <R extends IBaseResource> R populateResourceMetadata(
352                        IBaseResourceEntity<?> theEntitySource,
353                        boolean theForHistoryOperation,
354                        @Nullable Collection<? extends BaseTag> tagList,
355                        long theVersion,
356                        R theResourceTarget) {
357                if (theResourceTarget instanceof IResource) {
358                        IResource res = (IResource) theResourceTarget;
359                        theResourceTarget =
360                                        (R) populateResourceMetadataHapi(theEntitySource, tagList, theForHistoryOperation, res, theVersion);
361                } else {
362                        IAnyResource res = (IAnyResource) theResourceTarget;
363                        theResourceTarget =
364                                        populateResourceMetadataRi(theEntitySource, tagList, theForHistoryOperation, res, theVersion);
365                }
366                return theResourceTarget;
367        }
368
369        @SuppressWarnings("unchecked")
370        private <R extends IResource> R populateResourceMetadataHapi(
371                        IBaseResourceEntity<?> theEntity,
372                        @Nullable Collection<? extends BaseTag> theTagList,
373                        boolean theForHistoryOperation,
374                        R res,
375                        Long theVersion) {
376                R retVal = res;
377                if (theEntity.getDeleted() != null) {
378                        res = (R) myFhirContext.getResourceDefinition(res).newInstance();
379                        retVal = res;
380                        ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
381                        if (theForHistoryOperation) {
382                                ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE);
383                        }
384                } else if (theForHistoryOperation) {
385                        /*
386                         * 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.
387                         */
388                        Date published = theEntity.getPublished().getValue();
389                        Date updated = theEntity.getUpdated().getValue();
390                        if (published.equals(updated)) {
391                                ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST);
392                        } else {
393                                ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
394                        }
395                }
396
397                res.setId(theEntity.getIdDt().withVersion(theVersion.toString()));
398
399                ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion()));
400                ResourceMetadataKeyEnum.PUBLISHED.put(res, theEntity.getPublished());
401                ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated());
402                IDao.RESOURCE_PID.put(res, theEntity.getResourceId());
403
404                if (theTagList != null) {
405                        if (theEntity.isHasTags()) {
406                                TagList tagList = new TagList();
407                                List<IBaseCoding> securityLabels = new ArrayList<>();
408                                List<IdDt> profiles = new ArrayList<>();
409                                for (BaseTag next : theTagList) {
410                                        TagDefinition nextTag = next.getTag();
411                                        switch (nextTag.getTagType()) {
412                                                case PROFILE:
413                                                        profiles.add(new IdDt(nextTag.getCode()));
414                                                        break;
415                                                case SECURITY_LABEL:
416                                                        IBaseCoding secLabel =
417                                                                        (IBaseCoding) myFhirContext.getVersion().newCodingDt();
418                                                        secLabel.setSystem(nextTag.getSystem());
419                                                        secLabel.setCode(nextTag.getCode());
420                                                        secLabel.setDisplay(nextTag.getDisplay());
421                                                        secLabel.setVersion(nextTag.getVersion());
422                                                        Boolean userSelected = nextTag.getUserSelected();
423                                                        if (userSelected != null) {
424                                                                secLabel.setUserSelected(userSelected);
425                                                        }
426                                                        securityLabels.add(secLabel);
427                                                        break;
428                                                case TAG:
429                                                        Tag e = new Tag(nextTag.getSystem(), nextTag.getCode(), nextTag.getDisplay());
430                                                        e.setVersion(nextTag.getVersion());
431                                                        // careful! These are Boolean, not boolean.
432                                                        e.setUserSelectedBoolean(nextTag.getUserSelected());
433                                                        tagList.add(e);
434                                                        break;
435                                        }
436                                }
437                                if (tagList.size() > 0) {
438                                        ResourceMetadataKeyEnum.TAG_LIST.put(res, tagList);
439                                }
440                                if (securityLabels.size() > 0) {
441                                        ResourceMetadataKeyEnum.SECURITY_LABELS.put(res, toBaseCodingList(securityLabels));
442                                }
443                                if (profiles.size() > 0) {
444                                        ResourceMetadataKeyEnum.PROFILES.put(res, profiles);
445                                }
446                        }
447                }
448
449                return retVal;
450        }
451
452        @SuppressWarnings("unchecked")
453        private <R extends IBaseResource> R populateResourceMetadataRi(
454                        IBaseResourceEntity theEntity,
455                        @Nullable Collection<? extends BaseTag> theTagList,
456                        boolean theForHistoryOperation,
457                        IAnyResource res,
458                        Long theVersion) {
459                R retVal = (R) res;
460                if (theEntity.getDeleted() != null) {
461                        res = (IAnyResource) myFhirContext.getResourceDefinition(res).newInstance();
462                        retVal = (R) res;
463                        ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
464                        if (theForHistoryOperation) {
465                                ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE);
466                        }
467                } else if (theForHistoryOperation) {
468                        /*
469                         * 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.
470                         */
471                        Date published = theEntity.getPublished().getValue();
472                        Date updated = theEntity.getUpdated().getValue();
473                        if (published.equals(updated)) {
474                                ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST);
475                        } else {
476                                ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
477                        }
478                }
479
480                res.getMeta().setLastUpdated(null);
481                res.getMeta().setVersionId(null);
482
483                updateResourceMetadata(theEntity, res);
484                res.setId(res.getIdElement().withVersion(theVersion.toString()));
485
486                res.getMeta().setLastUpdated(theEntity.getUpdatedDate());
487                IDao.RESOURCE_PID.put(res, theEntity.getResourceId());
488
489                if (CollectionUtils.isNotEmpty(theTagList)) {
490                        res.getMeta().getTag().clear();
491                        res.getMeta().getProfile().clear();
492                        res.getMeta().getSecurity().clear();
493
494                        boolean haveWarnedForMissingTag = false;
495                        for (BaseTag next : theTagList) {
496                                if (next.getTag() == null) {
497                                        if (!haveWarnedForMissingTag) {
498                                                ourLog.warn(
499                                                                "Tag definition HFJ_TAG_DEF#{} is missing, returned Resource.meta may not be complete",
500                                                                next.getTagId());
501                                                haveWarnedForMissingTag = true;
502                                        }
503                                        continue;
504                                }
505
506                                switch (next.getTag().getTagType()) {
507                                        case PROFILE:
508                                                res.getMeta().addProfile(next.getTag().getCode());
509                                                break;
510                                        case SECURITY_LABEL:
511                                                IBaseCoding sec = res.getMeta().addSecurity();
512                                                sec.setSystem(next.getTag().getSystem());
513                                                sec.setCode(next.getTag().getCode());
514                                                sec.setDisplay(next.getTag().getDisplay());
515                                                break;
516                                        case TAG:
517                                                IBaseCoding tag = res.getMeta().addTag();
518                                                tag.setSystem(next.getTag().getSystem());
519                                                tag.setCode(next.getTag().getCode());
520                                                tag.setDisplay(next.getTag().getDisplay());
521                                                tag.setVersion(next.getTag().getVersion());
522                                                Boolean userSelected = next.getTag().getUserSelected();
523                                                // the tag is created with a null userSelected, but the api is primitive boolean.
524                                                // Only update if we are non-null.
525                                                if (nonNull(userSelected)) {
526                                                        tag.setUserSelected(userSelected);
527                                                }
528                                                break;
529                                }
530                        }
531                }
532
533                return retVal;
534        }
535
536        @Override
537        public void updateResourceMetadata(IBaseResourceEntity<?> theEntitySource, IBaseResource theResourceTarget) {
538                IIdType id = theEntitySource.getIdDt();
539                if (myFhirContext.getVersion().getVersion().isRi()) {
540                        id = myFhirContext.getVersion().newIdType().setValue(id.getValue());
541                }
542
543                if (id.hasResourceType() == false) {
544                        id = id.withResourceType(theEntitySource.getResourceType());
545                }
546
547                theResourceTarget.setId(id);
548                if (theResourceTarget instanceof IResource) {
549                        ResourceMetadataKeyEnum.VERSION.put(theResourceTarget, id.getVersionIdPart());
550                        ResourceMetadataKeyEnum.UPDATED.put(theResourceTarget, theEntitySource.getUpdated());
551                } else {
552                        IBaseMetaType meta = theResourceTarget.getMeta();
553                        meta.setVersionId(id.getVersionIdPart());
554                        meta.setLastUpdated(theEntitySource.getUpdatedDate());
555                }
556        }
557
558        private FhirContext getContext(FhirVersionEnum theVersion) {
559                Validate.notNull(theVersion, "theVersion must not be null");
560                if (theVersion == myFhirContext.getVersion().getVersion()) {
561                        return myFhirContext;
562                }
563                return FhirContext.forCached(theVersion);
564        }
565
566        private static String decodedResourceText(
567                        byte[] resourceBytes, String resourceText, ResourceEncodingEnum resourceEncoding) {
568                String decodedResourceText;
569                if (resourceText != null) {
570                        decodedResourceText = resourceText;
571                } else {
572                        decodedResourceText = decodeResource(resourceBytes, resourceEncoding);
573                }
574                return decodedResourceText;
575        }
576
577        private static List<BaseCodingDt> toBaseCodingList(List<IBaseCoding> theSecurityLabels) {
578                ArrayList<BaseCodingDt> retVal = new ArrayList<>(theSecurityLabels.size());
579                for (IBaseCoding next : theSecurityLabels) {
580                        retVal.add((BaseCodingDt) next);
581                }
582                return retVal;
583        }
584}