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