
001/*- 002 * #%L 003 * HAPI FHIR - Core Library 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.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 /** 052 * Resource.meta.source may include a request ID in the format sourceURI#requestID 053 * This method extracts the source URI component from the meta.source string 054 * 055 * @param theProvenanceSourceUri the meta.source string 056 * @return the source URI component, or a blank String if empty/null 057 */ 058 public static String extractSourceUriOrEmpty(String theProvenanceSourceUri) { 059 String sanitizedProvenance = defaultString(theProvenanceSourceUri); 060 return StringUtils.substringBefore(sanitizedProvenance, "#"); 061 } 062 063 /** 064 * @since 8.2.0 065 */ 066 public static String getSource(FhirContext theContext, IBaseResource theResource) { 067 return getSource(theContext, theResource.getMeta()); 068 } 069 070 public static String getSource(FhirContext theContext, IBaseMetaType theMeta) { 071 if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) { 072 return getSourceR4Plus(theContext, theMeta); 073 } else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) { 074 return getSourceDstu3((IBaseHasExtensions) theMeta); 075 } else { 076 throw new UnsupportedOperationException( 077 Msg.code(1782) + MetaUtil.class.getSimpleName() + ".getSource() not supported on FHIR Version " 078 + theContext.getVersion().getVersion()); 079 } 080 } 081 082 private static String getSourceDstu3(IBaseHasExtensions theMeta) { 083 IBaseHasExtensions metaWithExtensions = theMeta; 084 List<? extends IBaseExtension<?, ?>> extensions = metaWithExtensions.getExtension(); 085 for (IBaseExtension extension : extensions) { 086 if (HapiExtensions.EXT_META_SOURCE.equals(extension.getUrl())) { 087 IPrimitiveType<String> value = (IPrimitiveType<String>) extension.getValue(); 088 return value.getValueAsString(); 089 } 090 } 091 return null; 092 } 093 094 private static String getSourceR4Plus(FhirContext theFhirContext, IBaseMetaType theMeta) { 095 BaseRuntimeElementCompositeDefinition<?> elementDef = 096 (BaseRuntimeElementCompositeDefinition<?>) theFhirContext.getElementDefinition(theMeta.getClass()); 097 BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source"); 098 if (sourceChild == null) { 099 return null; 100 } 101 List<IBase> sourceValues = sourceChild.getAccessor().getValues(theMeta); 102 String retVal = null; 103 if (sourceValues.size() > 0) { 104 retVal = ((IPrimitiveType<?>) sourceValues.get(0)).getValueAsString(); 105 } 106 return retVal; 107 } 108 109 public static <R extends IBaseResource> void populateResourceSource( 110 FhirContext theFhirContext, String theProvenanceSourceUri, String theProvenanceRequestId, R theRetVal) { 111 String sourceString = extractSourceUriOrEmpty(theProvenanceSourceUri); 112 113 if (isNotBlank(theProvenanceRequestId)) { 114 sourceString = sourceString + "#" + theProvenanceRequestId; 115 } 116 117 if (isNotBlank(sourceString)) { 118 setSource(theFhirContext, theRetVal, sourceString); 119 } 120 } 121 122 /** 123 * Sets the value for <code>Resource.meta.source</code> for R4+ resources, and places the value in 124 * an extension on <code>Resource.meta</code> 125 * with the URL <code>http://hapifhir.io/fhir/StructureDefinition/resource-meta-source</code> for DSTU3. 126 * 127 * @param theContext The FhirContext object 128 * @param theResource The resource to modify 129 * @param theValue The source URI 130 * @see <a href="http://hl7.org/fhir/resource-definitions.html#Resource.meta">Meta.source</a> 131 */ 132 @SuppressWarnings("unchecked") 133 public static void setSource(FhirContext theContext, IBaseResource theResource, String theValue) { 134 if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) { 135 MetaUtil.setSource(theContext, theResource.getMeta(), theValue); 136 } else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) { 137 IBaseExtension<?, ?> sourceExtension = ((IBaseHasExtensions) theResource.getMeta()).addExtension(); 138 sourceExtension.setUrl(HapiExtensions.EXT_META_SOURCE); 139 IPrimitiveType<String> value = (IPrimitiveType<String>) 140 theContext.getElementDefinition("uri").newInstance(); 141 value.setValue(theValue); 142 sourceExtension.setValue(value); 143 } else { 144 ourLog.debug(MetaUtil.class.getSimpleName() + ".setSource() not supported on FHIR Version " 145 + theContext.getVersion().getVersion()); 146 } 147 } 148 149 public static void setSource(FhirContext theContext, IBaseMetaType theMeta, String theValue) { 150 BaseRuntimeElementCompositeDefinition<?> elementDef = 151 (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theMeta.getClass()); 152 BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source"); 153 List<IBase> sourceValues = sourceChild.getAccessor().getValues(theMeta); 154 IPrimitiveType<?> sourceElement; 155 if (sourceValues.size() > 0) { 156 sourceElement = ((IPrimitiveType<?>) sourceValues.get(0)); 157 } else { 158 sourceElement = 159 (IPrimitiveType<?>) theContext.getElementDefinition("uri").newInstance(); 160 sourceChild.getMutator().setValue(theMeta, sourceElement); 161 } 162 sourceElement.setValueAsString(theValue); 163 } 164 165 public static Set<String> getAutoVersionReferencesAtPath(IBaseMetaType theMeta, String theResourceType) { 166 return ExtensionUtil.getExtensionPrimitiveValues( 167 theMeta, HapiExtensions.EXTENSION_AUTO_VERSION_REFERENCES_AT_PATH) 168 .stream() 169 .map(path -> String.format("%s.%s", theResourceType, path)) 170 .collect(Collectors.toSet()); 171 } 172}