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