001/*-
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2024 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.util;
021
022import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
023import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
024import ca.uhn.fhir.context.FhirContext;
025import ca.uhn.fhir.context.FhirVersionEnum;
026import ca.uhn.fhir.i18n.Msg;
027import org.apache.commons.lang3.StringUtils;
028import org.hl7.fhir.instance.model.api.IBase;
029import org.hl7.fhir.instance.model.api.IBaseExtension;
030import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
031import org.hl7.fhir.instance.model.api.IBaseMetaType;
032import org.hl7.fhir.instance.model.api.IBaseResource;
033import org.hl7.fhir.instance.model.api.IPrimitiveType;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import java.util.List;
038import java.util.Set;
039import java.util.stream.Collectors;
040
041import static org.apache.commons.lang3.StringUtils.defaultString;
042import static org.apache.commons.lang3.StringUtils.isNotBlank;
043
044public class MetaUtil {
045        private static final Logger ourLog = LoggerFactory.getLogger(MetaUtil.class);
046
047        private MetaUtil() {
048                // non-instantiable
049        }
050
051        public static String cleanProvenanceSourceUriOrEmpty(String theProvenanceSourceUri) {
052                String sanitizedProvenance = defaultString(theProvenanceSourceUri);
053                return StringUtils.substringBefore(sanitizedProvenance, "#");
054        }
055
056        public static String getSource(FhirContext theContext, IBaseMetaType theMeta) {
057                if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
058                        return getSourceR4Plus(theContext, theMeta);
059                } else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
060                        return getSourceDstu3((IBaseHasExtensions) theMeta);
061                } else {
062                        throw new UnsupportedOperationException(
063                                        Msg.code(1782) + MetaUtil.class.getSimpleName() + ".getSource() not supported on FHIR Version "
064                                                        + theContext.getVersion().getVersion());
065                }
066        }
067
068        private static String getSourceDstu3(IBaseHasExtensions theMeta) {
069                IBaseHasExtensions metaWithExtensions = theMeta;
070                List<? extends IBaseExtension<?, ?>> extensions = metaWithExtensions.getExtension();
071                for (IBaseExtension extension : extensions) {
072                        if (HapiExtensions.EXT_META_SOURCE.equals(extension.getUrl())) {
073                                IPrimitiveType<String> value = (IPrimitiveType<String>) extension.getValue();
074                                return value.getValueAsString();
075                        }
076                }
077                return null;
078        }
079
080        private static String getSourceR4Plus(FhirContext theFhirContext, IBaseMetaType theMeta) {
081                BaseRuntimeElementCompositeDefinition<?> elementDef =
082                                (BaseRuntimeElementCompositeDefinition<?>) theFhirContext.getElementDefinition(theMeta.getClass());
083                BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source");
084                if (sourceChild == null) {
085                        return null;
086                }
087                List<IBase> sourceValues = sourceChild.getAccessor().getValues(theMeta);
088                String retVal = null;
089                if (sourceValues.size() > 0) {
090                        retVal = ((IPrimitiveType<?>) sourceValues.get(0)).getValueAsString();
091                }
092                return retVal;
093        }
094
095        public static <R extends IBaseResource> void populateResourceSource(
096                        FhirContext theFhirContext, String theProvenanceSourceUri, String theProvenanceRequestId, R theRetVal) {
097                String sourceString = cleanProvenanceSourceUriOrEmpty(theProvenanceSourceUri);
098                if (isNotBlank(theProvenanceRequestId)) {
099                        sourceString = sourceString + "#" + theProvenanceRequestId;
100                }
101
102                if (isNotBlank(sourceString)) {
103                        setSource(theFhirContext, theRetVal, sourceString);
104                }
105        }
106
107        /**
108         * Sets the value for <code>Resource.meta.source</code> for R4+ resources, and places the value in
109         * an extension on <code>Resource.meta</code>
110         * with the URL <code>http://hapifhir.io/fhir/StructureDefinition/resource-meta-source</code> for DSTU3.
111         *
112         * @param theContext  The FhirContext object
113         * @param theResource The resource to modify
114         * @param theValue    The source URI
115         * @see <a href="http://hl7.org/fhir/resource-definitions.html#Resource.meta">Meta.source</a>
116         */
117        @SuppressWarnings("unchecked")
118        public static void setSource(FhirContext theContext, IBaseResource theResource, String theValue) {
119                if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
120                        MetaUtil.setSource(theContext, theResource.getMeta(), theValue);
121                } else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
122                        IBaseExtension<?, ?> sourceExtension = ((IBaseHasExtensions) theResource.getMeta()).addExtension();
123                        sourceExtension.setUrl(HapiExtensions.EXT_META_SOURCE);
124                        IPrimitiveType<String> value = (IPrimitiveType<String>)
125                                        theContext.getElementDefinition("uri").newInstance();
126                        value.setValue(theValue);
127                        sourceExtension.setValue(value);
128                } else {
129                        ourLog.debug(MetaUtil.class.getSimpleName() + ".setSource() not supported on FHIR Version "
130                                        + theContext.getVersion().getVersion());
131                }
132        }
133
134        public static void setSource(FhirContext theContext, IBaseMetaType theMeta, String theValue) {
135                BaseRuntimeElementCompositeDefinition<?> elementDef =
136                                (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theMeta.getClass());
137                BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source");
138                List<IBase> sourceValues = sourceChild.getAccessor().getValues(theMeta);
139                IPrimitiveType<?> sourceElement;
140                if (sourceValues.size() > 0) {
141                        sourceElement = ((IPrimitiveType<?>) sourceValues.get(0));
142                } else {
143                        sourceElement =
144                                        (IPrimitiveType<?>) theContext.getElementDefinition("uri").newInstance();
145                        sourceChild.getMutator().setValue(theMeta, sourceElement);
146                }
147                sourceElement.setValueAsString(theValue);
148        }
149
150        public static Set<String> getAutoVersionReferencesAtPath(IBaseMetaType theMeta, String theResourceType) {
151                return ExtensionUtil.getExtensionPrimitiveValues(
152                                                theMeta, HapiExtensions.EXTENSION_AUTO_VERSION_REFERENCES_AT_PATH)
153                                .stream()
154                                .map(path -> String.format("%s.%s", theResourceType, path))
155                                .collect(Collectors.toSet());
156        }
157}