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