001package org.hl7.fhir.r5.utils.xver;
002
003import org.hl7.fhir.exceptions.FHIRException;
004import org.hl7.fhir.r5.context.IWorkerContext;
005import org.hl7.fhir.r5.model.ElementDefinition;
006import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
007import org.hl7.fhir.r5.model.Enumerations.FHIRVersion;
008import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
009import org.hl7.fhir.r5.model.StructureDefinition;
010import org.hl7.fhir.r5.model.StructureDefinition.ExtensionContextType;
011import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
012import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
013import org.hl7.fhir.r5.model.UriType;
014import org.hl7.fhir.utilities.Utilities;
015import org.hl7.fhir.utilities.VersionUtilities;
016import org.hl7.fhir.utilities.json.model.JsonElement;
017import org.hl7.fhir.utilities.json.model.JsonObject;
018import org.hl7.fhir.utilities.json.parser.JsonParser;
019import org.hl7.fhir.utilities.npm.PackageHacker;
020
021import java.io.IOException;
022import java.util.Date;
023import java.util.HashMap;
024import java.util.Map;
025
026public class XVerExtensionManagerOld extends XVerExtensionManager {
027
028  private Map<String, JsonObject> lists = new HashMap<>();
029
030  public XVerExtensionManagerOld(IWorkerContext context) {
031    super(context);
032  }
033
034  @Override
035  public XVerExtensionStatus status(String url) throws FHIRException {
036    if (url.length() < 54) {
037      return XVerExtensionStatus.Invalid;
038    }
039    url = url.replace("%5Bx%5D", "[x]");
040    String v = url.substring(20, 23);
041    String e = url.substring(54);
042    if (!lists.containsKey(v)) {
043      if (context.hasBinaryKey("xver-paths-"+v+".json")) {
044        try {
045          lists.put(v, JsonParser.parseObject(context.getBinaryForKey("xver-paths-"+v+".json")));
046        } catch (IOException e1) {
047          throw new FHIRException(e);
048        }
049      } else {
050        return XVerExtensionStatus.BadVersion;
051      }
052    }
053    JsonObject root = lists.get(v);
054    JsonObject path = root.getJsonObject(e);
055    if (path == null) {
056      path = root.getJsonObject(e+"[x]");      
057    }
058    if (path == null) {
059      return XVerExtensionStatus.Unknown;
060    }
061    if (path.has("elements") || path.has("types")) {
062      return XVerExtensionStatus.Valid;
063    } else {
064      return XVerExtensionStatus.Invalid;
065    }
066  }
067
068  @Override
069  public StructureDefinition getDefinition(String url) {
070    url = url.replace("%5Bx%5D", "[x]");
071    String verSource = url.substring(20, 23);
072    String verTarget = VersionUtilities.getMajMin(context.getVersion());
073    String e = url.substring(54);
074    String r = e.contains(".") ? e.substring(0, e.indexOf(".")) : e;
075    JsonObject root = lists.get(verSource);
076    JsonObject path = root.getJsonObject(e);
077    if (path == null) {
078      path = root.getJsonObject(e+"[x]");
079    }
080    
081    StructureDefinition sd = new StructureDefinition();
082    sd.setUserData(XVER_EXT_MARKER, "true");
083    sd.setUserData(XVER_VER_MARKER, verSource);
084    if (context.getResourceNamesAsSet().contains(r)) {
085      sd.setWebPath(Utilities.pathURL(context.getSpecUrl(), r.toLowerCase()+"-definitions.html#"+e));
086    } else {
087      sd.setWebPath(PackageHacker.fixPackageUrl("https://hl7.org/fhir/versions.html#extensions"));
088    }
089    sd.setUrl(url);
090    sd.setVersion(context.getVersion());
091    sd.setFhirVersion(FHIRVersion.fromCode(context.getVersion()));
092    sd.setKind(StructureDefinitionKind.COMPLEXTYPE);
093    sd.setType("Extension");
094    sd.setDerivation(TypeDerivationRule.CONSTRAINT);
095    sd.setName("Extension-"+verSource+"-"+e);
096    sd.setTitle("Extension Definition for "+e+" for Version "+verSource);
097    sd.setStatus(PublicationStatus.ACTIVE);
098    sd.setExperimental(false);
099    sd.setDate(new Date());
100    sd.setPublisher("FHIR Project");
101    sd.setPurpose("Defined so the validator can validate cross version extensions (see http://hl7.org/fhir/versions.html#extensions)");
102    sd.setAbstract(false);
103    sd.addContext().setType(ExtensionContextType.ELEMENT).setExpression(head(e));
104    sd.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/Extension");
105    if (path.has("types")) {
106      sd.getDifferential().addElement().setPath("Extension.extension").setMax("0");
107      sd.getDifferential().addElement().setPath("Extension.url").setFixed(new UriType(url));
108      ElementDefinition val = sd.getDifferential().addElement().setPath("Extension.value[x]").setMin(1);
109      populateTypes(path, val, verSource, verTarget);
110    } else if (path.has("elements")) {
111      for (JsonElement i : path.forceArray("elements").getItems()) {
112        String apath = e+"."+i.asString();
113        JsonObject elt = root.getJsonObject(apath);
114        if (elt != null) {
115          genExtensionContents(root, apath, verSource, verTarget, sd, i, elt, "Extension.extension");
116        }
117      }      
118      sd.getDifferential().addElement().setPath("Extension.url").setFixed(new UriType(url));
119      sd.getDifferential().addElement().setPath("Extension.value[x]").setMax("0");
120    } else {
121      throw new FHIRException("Internal error - attempt to define extension for "+url+" when it is invalid");
122    }
123    if (path.has("modifier") && path.asBoolean("modifier")) {
124      ElementDefinition baseDef = new ElementDefinition("Extension");
125      sd.getDifferential().getElement().add(0, baseDef);
126      baseDef.setIsModifier(true);
127    }
128    return sd;
129  }
130
131  private void genExtensionContents(JsonObject root, String apath, String verSource, String verTarget, StructureDefinition sd, JsonElement i, JsonObject elt, String epath) {
132    String s = i.asString().replace("[x]", "");
133    sd.getDifferential().addElement().setPath(epath).setSliceName(s);
134    if (elt.has("types")) {            
135      sd.getDifferential().addElement().setPath(epath+".extension").setMax("0");
136      sd.getDifferential().addElement().setPath(epath+".url").setFixed(new UriType(s));
137      ElementDefinition val = sd.getDifferential().addElement().setPath(epath+".value[x]").setMin(1);
138      populateTypes(elt, val, verSource, verTarget);
139    } else if (elt.has("elements")) {
140      for (JsonElement ic : elt.forceArray("elements").getItems()) { 
141        String apathC = apath+"."+ic.asString();
142        JsonObject eltC = root.getJsonObject(apathC);
143        if (eltC != null) {
144          genExtensionContents(root, apathC, verSource, verTarget, sd, ic, eltC, epath+".extension");
145        }
146      }
147      sd.getDifferential().addElement().setPath(epath+".url").setFixed(new UriType(s));
148      sd.getDifferential().addElement().setPath(epath+".value[x]").setMax("0");
149    } else {
150      throw new FHIRException("Internal error - unknown element "+apath);
151    }
152  }
153
154  private void populateTypes(JsonObject path, ElementDefinition val, String verSource, String verTarget) {
155    for (JsonElement i : path.forceArray("types").getItems()) {
156      String s = i.asString();
157      if (!s.startsWith("!")) {
158        if (s.contains("(")) {
159          String t = s.substring(0, s.indexOf("("));
160          TypeRefComponent tr = val.addType().setCode(translateDataType(verTarget, t));
161          if (hasTargets(tr.getCode()) ) {
162            s = s.substring(t.length()+1);
163            for (String p : s.substring(0, s.length()-1).split("\\|")) {
164              if ("Any".equals(p)) {
165                tr.addTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource");
166              } else if (p.contains(",")) {
167                for (String pp : p.split("\\,")) {
168                  if (isResource(pp)) {
169                    tr.addTargetProfile("http://hl7.org/fhir/StructureDefinition/"+pp);
170                  }
171                }
172              } else if (isResource(p)) {
173                tr.addTargetProfile("http://hl7.org/fhir/StructureDefinition/"+p);              
174              }
175            }
176          }
177        } else {
178          val.addType().setCode(translateDataType(verTarget, s));
179        }
180      }
181    }
182  }
183  private boolean isResource(String p) {
184    return context.getResourceNames().contains(p);
185  }
186
187  private boolean hasTargets(String dt) {
188    return Utilities.existsInList(dt, "canonical", "Reference", "CodeableReference");
189  }
190
191  private String translateDataType(String v, String dt) {
192    if (VersionUtilities.versionMatches("1.0.x", v) || VersionUtilities.versionMatches("1.4.x", v)) {
193      return translateToR2(dt);
194    } else if (VersionUtilities.versionMatches("3.0.x", v)) {
195      return translateToR3(dt);
196    } else {
197      return dt;
198    }
199  }
200
201  private String translateToR3(String dt) {
202    if ("canonical".equals(dt)) {
203      return "uri";
204    } else if ("url".equals(dt)) {
205      return "uri";
206    } else {
207      return dt;
208    }
209  }
210
211  private String translateToR2(String dt) {
212    if ("canonical".equals(dt)) {
213      return "uri";
214    } else if ("url".equals(dt)) {
215      return "uri";
216    } else if ("uuid".equals(dt)) {
217      return "id";
218    } else {
219      return dt;
220    }
221  }
222
223  private String head(String id) {
224    if (id.contains(".")) {
225      return id.substring(0, id.lastIndexOf("."));
226    } else {
227      return id;
228    }
229  }
230
231}