001package org.hl7.fhir.convertors.loaders.loaderR5;
002
003import java.io.IOException;
004import java.util.HashSet;
005import java.util.Set;
006
007import org.hl7.fhir.exceptions.FHIRException;
008import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
009import org.hl7.fhir.r5.context.IContextResourceLoader;
010import org.hl7.fhir.r5.extensions.ExtensionDefinitions;
011import org.hl7.fhir.r5.extensions.ExtensionUtilities;
012import org.hl7.fhir.r5.model.CanonicalResource;
013import org.hl7.fhir.r5.model.CanonicalType;
014import org.hl7.fhir.r5.model.ElementDefinition;
015import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
016import org.hl7.fhir.r5.model.OperationDefinition;
017import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent;
018import org.hl7.fhir.r5.model.Resource;
019import org.hl7.fhir.r5.model.StructureDefinition;
020import org.hl7.fhir.r5.model.UriType;
021import org.hl7.fhir.r5.model.ValueSet;
022import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
023import org.hl7.fhir.r5.utils.UserDataNames;
024import org.hl7.fhir.utilities.Utilities;
025import org.hl7.fhir.utilities.VersionUtilities;
026import org.hl7.fhir.utilities.npm.NpmPackage;
027import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
028
029import com.google.gson.JsonSyntaxException;
030
031import lombok.Getter;
032import lombok.Setter;
033import lombok.experimental.Accessors;
034
035@Accessors(chain = true)
036public abstract class BaseLoaderR5 implements IContextResourceLoader {
037
038  protected final String URL_BASE = "http://hl7.org/fhir/";
039
040  protected boolean patchUrls;
041  @Getter @Setter protected boolean killPrimitives;
042  @Getter protected Set<String> types = new HashSet<>();
043  protected ILoaderKnowledgeProviderR5 lkp;
044  private boolean loadProfiles = true;
045
046  public BaseLoaderR5(Set<String> types, ILoaderKnowledgeProviderR5 lkp) {
047    super();
048    this.types.addAll(types);
049    this.lkp = lkp;
050  }
051
052  public String getResourcePath(Resource resource) {
053    return lkp.getResourcePath(resource);
054  }
055
056  public void setPath(Resource r) {
057    String path = lkp.getResourcePath(r);
058    if (lkp.getWebRoot() != null) { 
059      r.setUserData(UserDataNames.render_webroot, lkp.getWebRoot());
060    } else {
061      r.setUserData(UserDataNames.render_webroot, "");      
062    }
063    if (path != null) {
064      r.setWebPath(path);
065    }
066  }
067
068  public IContextResourceLoader getNewLoader(NpmPackage npm) throws JsonSyntaxException, IOException {
069    BaseLoaderR5 ret = loaderFactory(npm);
070    ret.patchUrls = patchUrls;
071    ret.killPrimitives = killPrimitives;
072    return ret;
073  }
074
075  protected BaseLoaderR5 loaderFactory(NpmPackage npm) throws JsonSyntaxException, IOException {
076    if (VersionUtilities.isR5Plus(npm.fhirVersion())) {
077      return new R5ToR5Loader(types, lkp.forNewPackage(npm));
078    } else if (VersionUtilities.isR4BVer(npm.fhirVersion())) {
079      return new R4BToR5Loader(types, lkp.forNewPackage(npm), npm.version());
080    } else if (VersionUtilities.isR4Ver(npm.fhirVersion())) {
081      return new R4ToR5Loader(types, lkp.forNewPackage(npm), npm.version());
082    } else if (VersionUtilities.isR3Ver(npm.fhirVersion())) {
083      return new R3ToR5Loader(types, lkp.forNewPackage(npm));
084    } else if (VersionUtilities.isR2Ver(npm.fhirVersion())) {
085      return new R2ToR5Loader(types, lkp.forNewPackage(npm));
086    } else if (VersionUtilities.isR2BVer(npm.fhirVersion())) {
087      return new R2016MayToR5Loader(types, lkp.forNewPackage(npm));
088    } else {
089      throw new FHIRException("Unsupported FHIR Version " + npm.fhirVersion());
090    }
091  }
092
093  public boolean isPatchUrls() {
094    return patchUrls;
095  }
096
097  public void setPatchUrls(boolean patchUrls) {
098    this.patchUrls = patchUrls;
099  }
100
101  protected abstract String versionString();
102  
103
104  @Override
105  public String patchUrl(String url, String type) {
106    if (!patchUrls || url == null) {
107      return url;
108    } else if (url.startsWith("http://hl7.org/fhir/"+type+"/")) {
109      return "http://hl7.org/fhir/"+versionString()+"/"+url.substring(20);
110    } else if ("CodeSystem".equals(type) && url.startsWith("http://hl7.org/fhir/")) {      
111      return "http://hl7.org/fhir/"+versionString()+"/"+url.substring(20);
112    } else {
113      return url;
114    }
115  }
116
117  // we don't patch everything. It's quite hard work to do that,
118  // and we only patch URLs to support version transforms
119  // so we just patch sd/od -> vs -> cs
120  protected void doPatchUrls(Resource resource) {
121    resource.setUserData(UserDataNames.loader_urls_patched, true);
122    if (resource instanceof CanonicalResource) {
123      CanonicalResource cr = (CanonicalResource) resource;
124      cr.setUrl(patchUrl(cr.getUrl(), cr.fhirType()));
125      if (cr instanceof StructureDefinition) {
126        StructureDefinition sd = (StructureDefinition) cr;
127        sd.setBaseDefinition(patchUrl(sd.getBaseDefinition(), sd.fhirType()));
128        new ProfileUtilities(null, null, null, null).setIds(sd, false);
129        sd.addExtension().setUrl(ExtensionDefinitions.EXT_XML_NAMESPACE).setValue(new UriType(URL_BASE));
130        for (ElementDefinition ed : sd.getSnapshot().getElement())
131          patchUrl(ed);
132        for (ElementDefinition ed : sd.getDifferential().getElement())
133          patchUrl(ed);
134      }
135
136      if (cr instanceof ValueSet) {
137        ValueSet vs = (ValueSet) cr;
138        for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
139          inc.setSystem(patchUrl(inc.getSystem(), "CodeSystem"));
140        }
141        for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
142          inc.setSystem(patchUrl(inc.getSystem(), "CodeSystem"));
143        }        
144      }
145      if (cr instanceof OperationDefinition) {
146        OperationDefinition od = (OperationDefinition) cr;
147        for (OperationDefinitionParameterComponent param : od.getParameter()) {
148          patchUrls(param);
149        }        
150      }
151    }
152  }
153  
154  private void patchUrls(OperationDefinitionParameterComponent param) {
155    if (param.hasBinding()) {
156      param.getBinding().setValueSet(patchUrl(param.getBinding().getValueSet(), "ValueSet"));      
157    }
158    for (OperationDefinitionParameterComponent p : param.getPart()) {
159      patchUrls(p);
160    }
161  }
162
163  private void patchUrl(ElementDefinition ed) {
164    for (TypeRefComponent tr : ed.getType()) {
165      if (!Utilities.isAbsoluteUrl(tr.getCode())) {
166        tr.setCode(URL_BASE+versionString()+"/StructureDefinition/"+tr.getCode());
167      }
168      for (CanonicalType s : tr.getTargetProfile()) {
169        s.setValue(patchUrl(s.getValue(), "StructureDefinition"));
170      }
171      if (tr.hasExtension(ExtensionDefinitions.EXT_FHIR_TYPE)) {
172        String code = ExtensionUtilities.readStringExtension(tr, ExtensionDefinitions.EXT_FHIR_TYPE);
173        String url = URL_BASE+versionString()+"/StructureDefinition/"+code;
174        ExtensionUtilities.setUrlExtension(tr, ExtensionDefinitions.EXT_FHIR_TYPE, url);
175      }
176      for (CanonicalType c : tr.getProfile()) {
177        c.setValue(patchUrl(c.getValue(), "StructureDefinition"));
178      }
179      for (CanonicalType c : tr.getTargetProfile()) {
180        c.setValue(patchUrl(c.getValue(), "StructureDefinition"));
181      }
182    }
183    if (ed.hasBinding()) {
184      ed.getBinding().setValueSet(patchUrl(ed.getBinding().getValueSet(), "ValueSet"));
185    }
186    if (ed.hasContentReference()) {
187      ed.setContentReference(patchUrl(ed.getContentReference(), "StructureDefinition"));
188    }
189  }
190
191  public IContextResourceLoader setLoadProfiles(boolean value) {
192    loadProfiles = value;
193    return this;
194  }
195  
196  public boolean wantLoad(NpmPackage pi, PackageResourceInformation pri) {
197    if (pri.getResourceType().equals("StructureDefinition")) {
198      if (loadProfiles) {
199        return true;
200      } else {
201        return pi.isCore() && Utilities.tail(pri.getUrl()).equals(pri.getStatedType());
202      }
203    } else if (pi.isCore() && "CodeSystem".equals(pri.getResourceType()) && "spdx-license".equals(pri.getId())) {
204      return false;
205    } else {
206      return true;
207    }
208  }
209}