001package org.hl7.fhir.convertors.misc;
002
003import java.io.File;
004import java.io.FileInputStream;
005import java.io.FileNotFoundException;
006import java.io.IOException;
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.HashSet;
010import java.util.List;
011import java.util.Map;
012import java.util.Map.Entry;
013import java.util.Set;
014
015import javax.xml.parsers.ParserConfigurationException;
016
017import org.hl7.fhir.dstu2.model.ElementDefinition;
018import org.hl7.fhir.dstu2.model.StructureDefinition;
019import org.hl7.fhir.dstu2.model.StructureDefinition.StructureDefinitionSnapshotComponent;
020import org.hl7.fhir.exceptions.FHIRFormatError;
021import org.hl7.fhir.utilities.Utilities;
022import org.hl7.fhir.utilities.json.JsonTrackingParser;
023import org.xml.sax.SAXException;
024
025import com.google.gson.JsonArray;
026import com.google.gson.JsonElement;
027import com.google.gson.JsonObject;
028
029public class XVerPackegeFixer {
030  
031  public static class References {
032    private boolean modifier;
033    private boolean inherited;
034
035    public boolean getModifier() {
036      return modifier;
037    }
038
039    public void setModifier(boolean value) {
040      this.modifier = value;
041    }
042
043    public boolean getInherited() {
044      return inherited;
045    }
046
047    public void setInherited(boolean inherited) {
048      this.inherited = inherited;
049    }
050    
051  }
052
053
054  private static final String R5_FOLDER =  "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r5.core/package";
055  private static final String R4_FOLDER =  "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r4.core/package";
056  private static final String R3_FOLDER =  "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r3.core/package";
057  private static final String R2B_FOLDER = "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r2b.core/package";
058  private static final String R2_FOLDER =  "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r2.core/package";
059  private static int modCount;
060  
061  private static Map<String, org.hl7.fhir.r5.model.StructureDefinition> map5 = new HashMap<>();
062  private static Map<String, org.hl7.fhir.r4.model.StructureDefinition> map4 = new HashMap<>();
063  private static Map<String, org.hl7.fhir.dstu3.model.StructureDefinition> map3 = new HashMap<>();
064  private static Map<String, org.hl7.fhir.dstu2.model.StructureDefinition> map2 = new HashMap<>();
065  private static Map<String, org.hl7.fhir.dstu2016may.model.StructureDefinition> map2b = new HashMap<>();
066
067  public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
068    modCount = 0;
069    for (File f : new File(args[0]).listFiles()) {
070      if (f.getName().startsWith("xver-") && f.getName().contains("1.0")) {
071        JsonObject j = JsonTrackingParser.parseJson(f);
072        fixUp(j, f.getName());
073        JsonTrackingParser.write(j, f, true);
074      }
075    }
076    System.out.println("all done: "+modCount+" modifiers");
077  }
078
079  private static void fixUp(JsonObject j, String name) throws FHIRFormatError, FileNotFoundException, IOException {
080    name = name.replace(".json",  "");
081    System.out.println("Process "+name);
082    String version = name.substring(name.lastIndexOf("-")+1);
083    Set<String> pr = new HashSet<>();
084    
085    int i = 0;
086    for (Entry<String, JsonElement> e : j.entrySet()) {
087      if (i == 50) {
088        i = 0;
089        System.out.print(".");
090      }
091      i++;
092      String n = e.getKey();
093      JsonObject o = ((JsonObject) e.getValue());
094      // boolean ok = (o.has("types") && o.getAsJsonArray("types").size() > 0) || (o.has("elements") && o.getAsJsonArray("elements").size() > 0);
095//      if (!ok) {
096      if (o.has("types")) {
097        o.remove("types");
098      }
099      if (o.has("elements")) {
100        o.remove("elements");
101      }
102      if (o.has("modifier")) {
103        o.remove("modifier");
104      }
105        List<String> types = new ArrayList<>(); 
106        List<String> elements = new ArrayList<>();
107        References mod = new References();
108        getElementInfo(version, n, types, elements, mod);
109        if (mod.getInherited()) {
110          pr.add(n);
111        }
112        if (elements.size() > 0) {
113          JsonArray arr = o.getAsJsonArray("elements");
114          if (arr == null) {
115            arr = new JsonArray();
116            o.add("elements", arr);
117          }
118          for (String s : elements) {
119            arr.add(s);
120          }
121        } else if (types.size() > 0) {
122          JsonArray arr = o.getAsJsonArray("types");
123          if (arr == null) {
124            arr = new JsonArray();
125            o.add("types", arr);
126          }
127          for (String s : types) {
128            arr.add(s);
129          }
130        }
131        if (mod.getModifier()) {
132          o.addProperty("modifier", true);
133          modCount++;
134        }
135//      }
136    }    
137    for (String s : pr) {
138      j.remove(s);
139    }
140    System.out.println("done");
141  }
142
143  private static boolean getElementInfo(String version, String n, List<String> types, List<String> elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException {
144//    if ("contained".equals(n.substring(n.indexOf(".")+1))) {
145//      return false;
146//    }
147    switch (version) {
148    case "4.6": return getElementInfoR5(n, types, elements, mod);
149    case "4.0": return getElementInfoR4(n, types, elements, mod);
150    case "3.0": return getElementInfoR3(n, types, elements, mod);
151    case "1.4": return getElementInfoR2B(n, types, elements, mod);
152    case "1.0": return getElementInfoR2(n, types, elements, mod);
153    }
154    return false;
155  }
156
157  private static String tail(String value) {
158    return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value;
159  }
160
161  private static boolean getElementInfoR5(String n, List<String> types, List<String> elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException {    
162    String tn = n.substring(0, n.indexOf("."));
163    org.hl7.fhir.r5.model.StructureDefinition sd = null;
164    if (map5.containsKey(tn)) {
165      sd = map5.get(tn);
166    } else {
167      sd = (org.hl7.fhir.r5.model.StructureDefinition) new org.hl7.fhir.r5.formats.JsonParser().parse(new FileInputStream(Utilities.path(R5_FOLDER, "StructureDefinition-"+tn+".json")));
168      map5.put(tn, sd);
169    }
170    for (org.hl7.fhir.r5.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
171      if (ed.getPath().equals(n)) {
172        mod.setModifier(ed.getIsModifier());
173       List<org.hl7.fhir.r5.model.ElementDefinition> children = listChildrenR5(sd.getSnapshot().getElement(), ed);
174        if (children.size() > 0) {
175          for (org.hl7.fhir.r5.model.ElementDefinition c : children) {
176            String en = c.getPath().substring(ed.getPath().length()+1);
177            elements.add(en);
178          }
179        } else {
180          for (org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
181            if (t.hasTargetProfile()) {
182              StringBuilder b = new StringBuilder();
183              b.append(t.getWorkingCode());
184              b.append("(");
185              boolean first = true;
186              for (org.hl7.fhir.r5.model.CanonicalType u : t.getTargetProfile()) {
187                if (first) first = false; else b.append("|");
188                b.append(tail(u.getValue()));
189              }
190              b.append(")");
191              types.add(b.toString());
192            } else {
193              types.add(t.getWorkingCode());
194            }
195          }
196        }
197      }
198    }
199    return false;
200  }
201
202
203  private static List<org.hl7.fhir.r5.model.ElementDefinition> listChildrenR5(List<org.hl7.fhir.r5.model.ElementDefinition> list, org.hl7.fhir.r5.model.ElementDefinition ed) {
204    List<org.hl7.fhir.r5.model.ElementDefinition> res = new ArrayList<>();
205    for (org.hl7.fhir.r5.model.ElementDefinition t : list) {
206      String p = t.getPath();
207      if (p.startsWith(ed.getPath()+".")) {
208        p = p.substring(ed.getPath().length()+1);
209        if (!p.contains(".")) {
210          res.add(t);
211        }
212      }
213    }
214    return res;
215  }
216  
217  private static boolean getElementInfoR4(String n, List<String> types, List<String> elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException {    
218    String tn = n.substring(0, n.indexOf("."));
219    org.hl7.fhir.r4.model.StructureDefinition sd = null;
220    if (map4.containsKey(tn)) {
221      sd = map4.get(tn);
222    } else {
223      sd = (org.hl7.fhir.r4.model.StructureDefinition) new org.hl7.fhir.r4.formats.JsonParser().parse(new FileInputStream(Utilities.path(R4_FOLDER, "StructureDefinition-"+tn+".json")));
224      map4.put(tn, sd);
225    }
226    for (org.hl7.fhir.r4.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
227      if (ed.getPath().equals(n)) {
228        mod.setModifier(ed.getIsModifier());
229        List<org.hl7.fhir.r4.model.ElementDefinition> children = listChildrenR4(sd.getSnapshot().getElement(), ed);
230        if (children.size() > 0) {
231          for (org.hl7.fhir.r4.model.ElementDefinition c : children) {
232            String en = c.getPath().substring(ed.getPath().length()+1);
233            elements.add(en);
234          }
235        } else {
236          for (org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
237            if (t.hasTargetProfile()) {
238              StringBuilder b = new StringBuilder();
239              b.append(t.getWorkingCode());
240              b.append("(");
241              boolean first = true;
242              for (org.hl7.fhir.r4.model.CanonicalType u : t.getTargetProfile()) {
243                if (first) first = false; else b.append("|");
244                b.append(tail(u.getValue()));
245              }
246              b.append(")");
247              types.add(b.toString());
248            } else {
249              types.add(t.getWorkingCode());
250            }
251          }
252        }
253      }
254    }
255    return false;
256  }
257
258
259  private static List<org.hl7.fhir.r4.model.ElementDefinition> listChildrenR4(List<org.hl7.fhir.r4.model.ElementDefinition> list, org.hl7.fhir.r4.model.ElementDefinition ed) {
260    List<org.hl7.fhir.r4.model.ElementDefinition> res = new ArrayList<>();
261    for (org.hl7.fhir.r4.model.ElementDefinition t : list) {
262      String p = t.getPath();
263      if (p.startsWith(ed.getPath()+".")) {
264        p = p.substring(ed.getPath().length()+1);
265        if (!p.contains(".")) {
266          res.add(t);
267        }
268      }
269    }
270    return res;
271  }
272 
273
274  private static boolean getElementInfoR3(String n, List<String> types, List<String> elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException {    
275    String tn = n.substring(0, n.indexOf("."));
276    org.hl7.fhir.dstu3.model.StructureDefinition sd = null;
277    if (map3.containsKey(tn)) {
278      sd = map3.get(tn);
279    } else {
280      sd = (org.hl7.fhir.dstu3.model.StructureDefinition) new org.hl7.fhir.dstu3.formats.JsonParser().parse(new FileInputStream(Utilities.path(R3_FOLDER, "StructureDefinition-"+tn+".json")));
281      map3.put(tn, sd);
282    }
283    for (org.hl7.fhir.dstu3.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
284      if (ed.getPath().equals(n)) {
285        mod.setModifier(ed.getIsModifier());
286        List<org.hl7.fhir.dstu3.model.ElementDefinition> children = listChildrenR3(sd.getSnapshot().getElement(), ed);
287        if (children.size() > 0) {
288          for (org.hl7.fhir.dstu3.model.ElementDefinition c : children) {
289            String en = c.getPath().substring(ed.getPath().length()+1);
290            elements.add(en);
291          }
292        } else {
293          for (org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
294            if (t.hasTargetProfile()) {
295              StringBuilder b = new StringBuilder();
296              b.append(t.getCode());
297              b.append("(");
298              b.append(tail(t.getTargetProfile()));
299              b.append(")");
300              types.add(b.toString());
301            } else {
302              types.add(t.getCode());
303            }
304          }
305        }
306      }
307    }
308    return false;
309  }
310
311
312  private static List<org.hl7.fhir.dstu3.model.ElementDefinition> listChildrenR3(List<org.hl7.fhir.dstu3.model.ElementDefinition> list, org.hl7.fhir.dstu3.model.ElementDefinition ed) {
313    List<org.hl7.fhir.dstu3.model.ElementDefinition> res = new ArrayList<>();
314    for (org.hl7.fhir.dstu3.model.ElementDefinition t : list) {
315      String p = t.getPath();
316      if (p.startsWith(ed.getPath()+".")) {
317        p = p.substring(ed.getPath().length()+1);
318        if (!p.contains(".")) {
319          res.add(t);
320        }
321      }
322    }
323    return res;
324  }
325  
326
327  private static boolean getElementInfoR2(String n, List<String> types, List<String> elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException {    
328    String tn = n.substring(0, n.indexOf("."));
329    org.hl7.fhir.dstu2.model.StructureDefinition sd = null;
330    sd = loadType(tn);
331    mod.setInherited(isInherited(n, sd));
332    for (org.hl7.fhir.dstu2.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
333      if (ed.getPath().equals(n)) {
334        mod.setModifier(ed.getIsModifier());
335        List<org.hl7.fhir.dstu2.model.ElementDefinition> children = listChildrenR2(sd.getSnapshot().getElement(), ed);
336        if (children.size() > 0) {
337          for (org.hl7.fhir.dstu2.model.ElementDefinition c : children) {
338            String en = c.getPath().substring(ed.getPath().length()+1);
339            elements.add(en);
340          }
341        } else {
342          boolean isReference = false;
343          StringBuilder r = new StringBuilder();
344          r.append("Reference(");
345          for (org.hl7.fhir.dstu2.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
346            if (t.hasProfile()) {
347              if ("Reference".equals(t.getCode())) {
348                for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) {
349                  if (isReference) 
350                    r.append("|");
351                  r.append(tail(u.getValue()));
352                  isReference = true;
353                }              
354              } else {
355                StringBuilder b = new StringBuilder();
356                b.append(t.getCode());
357                b.append("(");
358                boolean first = true;
359                for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) {
360                  if (first) first = false; else b.append("|");
361                  b.append(tail(u.getValue()));
362                }
363                b.append(")");
364                types.add(b.toString());
365              }
366            } else {
367              types.add(t.getCode());
368            }
369          }
370          if (isReference) {
371            r.append(")");
372            types.add(r.toString());              
373          }
374        }
375      }
376    }
377    return false;
378  }
379
380  private static org.hl7.fhir.dstu2.model.StructureDefinition loadType(String tn)
381      throws IOException, FileNotFoundException {
382    org.hl7.fhir.dstu2.model.StructureDefinition sd;
383    if (map2.containsKey(tn)) {
384      sd = map2.get(tn);
385    } else {
386      sd = (org.hl7.fhir.dstu2.model.StructureDefinition) new org.hl7.fhir.dstu2.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2_FOLDER, "StructureDefinition-"+tn+".json")));
387      map2.put(tn, sd);
388    }
389    return sd;
390  }
391
392
393  private static boolean isInherited(String n, StructureDefinition sd) throws FileNotFoundException, IOException {
394    String tail = n.substring(n.indexOf(".")+1);
395    while (sd != null) {
396      sd = sd.hasBase() ? loadType(tail(sd.getBase())) : null;
397      if (sd != null && hasPath(sd.getSnapshot(), sd.getName()+"."+tail)) {
398        return true;
399      }
400    }
401    return false;
402  }
403
404  private static boolean hasPath(StructureDefinitionSnapshotComponent snapshot, String path) {
405    for (ElementDefinition ed : snapshot.getElement()) {
406      if (ed.getPath().equals(path)) {
407        return true;
408      }
409    }
410    return false;
411  }
412
413  private static List<org.hl7.fhir.dstu2.model.ElementDefinition> listChildrenR2(List<org.hl7.fhir.dstu2.model.ElementDefinition> list, org.hl7.fhir.dstu2.model.ElementDefinition ed) {
414    List<org.hl7.fhir.dstu2.model.ElementDefinition> res = new ArrayList<>();
415    for (org.hl7.fhir.dstu2.model.ElementDefinition t : list) {
416      String p = t.getPath();
417      if (p.startsWith(ed.getPath()+".")) {
418        p = p.substring(ed.getPath().length()+1);
419        if (!p.contains(".")) {
420          res.add(t);
421        }
422      }
423    }
424    return res;
425  }
426  
427
428  private static boolean getElementInfoR2B(String n, List<String> types, List<String> elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException {    
429    String tn = n.substring(0, n.indexOf("."));
430    org.hl7.fhir.dstu2016may.model.StructureDefinition sd = null;
431    if (map2b.containsKey(tn)) {
432      sd = map2b.get(tn);
433    } else {
434      sd = (org.hl7.fhir.dstu2016may.model.StructureDefinition) new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2B_FOLDER, "StructureDefinition-"+tn+".json")));
435      map2b.put(tn, sd);
436    }
437    for (org.hl7.fhir.dstu2016may.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
438      if (ed.getPath().equals(n)) {
439        mod.setModifier(ed.getIsModifier());
440        List<org.hl7.fhir.dstu2016may.model.ElementDefinition> children = listChildrenR2B(sd.getSnapshot().getElement(), ed);
441        if (children.size() > 0) {
442          for (org.hl7.fhir.dstu2016may.model.ElementDefinition c : children) {
443            String en = c.getPath().substring(ed.getPath().length()+1);
444            elements.add(en);
445          }
446        } else {
447          for (org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
448            if (t.hasProfile()) {
449              StringBuilder b = new StringBuilder();
450              b.append(t.getCode());
451              b.append("(");
452              boolean first = true;
453              for (org.hl7.fhir.dstu2016may.model.UriType u : t.getProfile()) {
454                if (first) first = false; else b.append("|");
455                b.append(tail(u.getValue()));
456              }              
457              b.append(")");
458              types.add(b.toString());
459            } else {
460              types.add(t.getCode());
461            }
462          }
463        }
464      }
465    }
466    return false;
467  }
468
469
470  private static List<org.hl7.fhir.dstu2016may.model.ElementDefinition> listChildrenR2B(List<org.hl7.fhir.dstu2016may.model.ElementDefinition> list, org.hl7.fhir.dstu2016may.model.ElementDefinition ed) {
471    List<org.hl7.fhir.dstu2016may.model.ElementDefinition> res = new ArrayList<>();
472    for (org.hl7.fhir.dstu2016may.model.ElementDefinition t : list) {
473      String p = t.getPath();
474      if (p.startsWith(ed.getPath()+".")) {
475        p = p.substring(ed.getPath().length()+1);
476        if (!p.contains(".")) {
477          res.add(t);
478        }
479      }
480    }
481    return res;
482  }
483  
484
485}