001package org.hl7.fhir.convertors.misc;
002
003import java.io.File;
004import java.io.FileNotFoundException;
005import java.io.FileOutputStream;
006import java.io.IOException;
007import java.util.ArrayList;
008import java.util.HashSet;
009import java.util.Set;
010
011import org.hl7.fhir.exceptions.FHIRFormatError;
012import org.hl7.fhir.r5.context.CanonicalResourceManager;
013import org.hl7.fhir.r5.formats.IParser.OutputStyle;
014import org.hl7.fhir.r5.formats.JsonParser;
015import org.hl7.fhir.r5.formats.XmlParser;
016import org.hl7.fhir.r5.model.CanonicalResource;
017import org.hl7.fhir.r5.model.CodeSystem;
018import org.hl7.fhir.r5.model.ElementDefinition;
019import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent;
020import org.hl7.fhir.r5.model.StructureDefinition;
021import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionContextComponent;
022import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
023import org.hl7.fhir.r5.model.ValueSet;
024import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
025import org.hl7.fhir.utilities.Utilities;
026import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
027import org.hl7.fhir.utilities.json.model.JsonObject;
028import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
029import org.hl7.fhir.utilities.npm.NpmPackage;
030import org.hl7.fhir.utilities.xml.XMLUtil;
031import org.w3c.dom.Element;
032
033public class ExtensionExtractor {
034
035  public static void main(String[] args) throws FHIRFormatError, FileNotFoundException, IOException {
036    new ExtensionExtractor().process(args[0]);
037  }
038
039  private void process(String dst) throws IOException {
040    Set<String> ids = new HashSet<>();
041    FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder().build();
042    NpmPackage r5 = pcm.loadPackage("hl7.fhir.r5.core",  "current");
043    CanonicalResourceManager<CodeSystem> cslist = new CanonicalResourceManager<CodeSystem>(true, false);
044    for (String r : r5.listResources("CodeSystem")) {
045      CodeSystem cs = (CodeSystem) new JsonParser().parse(r5.load(r));
046      cslist.see(cs, null);
047    }
048    CanonicalResourceManager<ValueSet> vslist = new CanonicalResourceManager<ValueSet>(true, false);
049    for (String r : r5.listResources("ValueSet")) {
050      ValueSet vs = (ValueSet) new JsonParser().parse(r5.load(r));
051      vslist.see(vs, null);
052    }
053    for (ValueSet vs : vslist.getList()) {
054      for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
055        CodeSystem cs = cslist.get(inc.getSystem());
056        if (cs != null) {
057          if (!cs.hasUserData("vsl")) {
058            cs.setUserData("vsl", new ArrayList<ValueSet>()); 
059          }
060          ((ArrayList<ValueSet>) cs.getUserData("vsl")).add(vs);
061        }
062      }
063    }
064    for (String r : r5.listResources("StructureDefinition")) {
065      StructureDefinition sd = (StructureDefinition) new JsonParser().parse(r5.load(r));
066      if (sd.getType().equals("Extension") && sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
067        for (ElementDefinition ed : sd.getSnapshot().getElement()) {
068          seeBinding(ed.getBinding().getValueSet(), vslist, "ext", sd);
069          for (ElementDefinitionBindingAdditionalComponent ab : ed.getBinding().getAdditional()) {
070            seeBinding(ab.getValueSet(), vslist, "ext", sd);
071          }
072        }
073        sd.setSnapshot(null);
074        String fn;
075        if (sd.getContext().size() == 0) {
076          save(sd, dst,"none", ids);
077        } else if (sd.getContext().size() > 1) {
078          boolean dt = true;
079          for (StructureDefinitionContextComponent x : sd.getContext()) {
080            String s = extractType(x.getExpression());
081            dt = dt && isDataType(s);
082          }
083          if (dt) {
084            save(sd, dst,"datatypes", ids);
085          } else {
086            save(sd, dst,"multiple", ids);
087          }
088        } else {
089          String s = extractType(sd.getContextFirstRep().getExpression());
090          if (isDataType(s)) {
091            save(sd, dst,"datatypes", ids);
092          } else {
093            save(sd, dst,s, ids);
094          }
095        }
096      } else {
097        for (ElementDefinition ed : sd.getSnapshot().getElement()) {
098          seeBinding(ed.getBinding().getValueSet(), vslist, "core", sd);
099          for (ElementDefinitionBindingAdditionalComponent ab : ed.getBinding().getAdditional()) {
100            seeBinding(ab.getValueSet(), vslist, "core", sd);
101          }
102        }
103      }
104    }
105    for (ValueSet vs : vslist.getList()) {
106      if (vs.hasUserData("core") || !vs.hasUserData("ext") || vs.getUrl().startsWith("http://terminology.")) {
107        vslist.drop(vs.getId());
108      }
109    }
110    for (CodeSystem cs : cslist.getList()) {
111      boolean keep = false;
112      if (cs.hasUserData("vsl") && !cs.getUrl().startsWith("http://terminology.")) {
113        keep = true;
114        for (ValueSet vs : (ArrayList<ValueSet>) cs.getUserData("vsl")) {
115          if (!vslist.has(vs.getUrl())) {
116            keep = false;
117          }
118        }
119      }
120      if (!keep) {
121        cslist.drop(cs.getId());
122      }
123    }
124    for (ValueSet vs : vslist.getList()) {
125      StructureDefinition sd = (StructureDefinition) vs.getUserData("ext");
126      String s = sd.getUserString("folder");
127      save(vs, dst, s, ids);
128    }
129    for (CodeSystem cs : cslist.getList()) {
130      ValueSet vs = ((ArrayList<ValueSet>) cs.getUserData("vsl")).get(0);
131      String s = vs.getUserString("folder");
132      save(cs, dst,s, ids);
133    }
134    
135    deleteMatchingResources(ids, ManagedFileAccess.file("/Users/grahamegrieve/work/r5/source"));
136  }
137
138  private void deleteMatchingResources(Set<String> ids, File folder) {
139    for (File f : folder.listFiles()) {
140      if (f.isDirectory()) {
141        deleteMatchingResources(ids, f);
142      } else if (f.getName().endsWith(".json")) {
143        try {
144          JsonObject json = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(f);
145          if (json.has("resourceType") && json.has("id") && ids.contains(json.asString("id"))) {
146            System.out.println("Delete "+f.getAbsolutePath());
147            f.delete();
148          }
149        } catch (Exception e) {
150          // nothing
151        }
152      } else if (f.getName().endsWith(".xml")) {
153        try {
154          Element xml = XMLUtil.parseFileToDom(f.getAbsolutePath()).getDocumentElement();
155          if (XMLUtil.hasNamedChild(xml, "id") && ids.contains(XMLUtil.getNamedChildValue(xml, "id"))) {
156            System.out.println("Delete "+f.getAbsolutePath());
157            f.delete();
158          }
159        } catch (Exception e) {
160          // nothing
161        }
162      }
163    }
164    
165  }
166
167  private void save(CanonicalResource cr, String dst, String folder, Set<String> ids) throws IOException {
168    // TODO Auto-generated method stub
169    cr.setText(null);
170    if (!cr.hasTitle()) {
171      cr.setTitle(Utilities.unCamelCase(cr.getName()));
172    }
173    ids.add(cr.getId());
174    String fn = Utilities.path(dst, folder, cr.fhirType()+"-"+cr.getId()+".xml"); 
175    cr.setUserData("folder", folder);
176    if (!ManagedFileAccess.file(fn).exists()) {
177      Utilities.createDirectory(Utilities.getDirectoryForFile(fn));
178      new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(fn), cr);
179    }
180  }
181
182  private void seeBinding(String url, CanonicalResourceManager<ValueSet> vslist, String name, StructureDefinition sd) {
183    ValueSet vs = vslist.get(url);
184    if (vs != null) { 
185      vs.setUserData(name, sd);
186    }    
187  }
188
189  private boolean isDataType(String s) {
190    return Utilities.existsInList(s,
191        "PrimitiveType", "instant", "time", "date", "dateTime", "decimal", "boolean", "integer", "string",
192        "uri", "base64Binary", "code", "id", "oid", "unsignedInt", "positiveInt", "markdown", "url", "canonical",
193        "uuid", "integer64", "DataType", "BackboneType", "Identifier", "HumanName", "Address", "ContactPoint",
194        "Timing", "Quantity", "SimpleQuantity", "Attachment", "Range", "Period", "Ratio", "RatioRange", "CodeableConcept",
195        "Coding", "SampledData", "Age", "Distance", "Duration", "Count", "Money", "MoneyQuantity", "Annotation", "Signature", "DataType",
196        "ContactDetail", "Contributor", "DataRequirement", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext",
197        "Expression", "ExtendedContactDetail", "VirtualServiceDetail", "Availability", "MonetaryComponent", "DataType",
198        "BackboneType", "Reference", "Narrative", "Extension", "Meta", "ElementDefinition", "Dosage", "xhtml", "CodeableReference");
199  }
200
201  private String extractType(String x) {
202    String s = x;
203    if (s.contains(".")) {
204      s = s.substring(0, s.indexOf("."));
205    }
206    return s;
207  }
208}