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