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