001package org.hl7.fhir.r4.utils; 002 003import java.io.FileWriter; 004import java.io.IOException; 005import java.io.InputStream; 006import java.util.ArrayList; 007import java.util.Collection; 008import java.util.Collections; 009import java.util.Comparator; 010import java.util.HashMap; 011import java.util.List; 012import java.util.Map; 013 014import org.hl7.fhir.exceptions.FHIRFormatError; 015import org.hl7.fhir.r4.formats.JsonParser; 016import org.hl7.fhir.r4.model.CodeType; 017import org.hl7.fhir.r4.model.ElementDefinition; 018import org.hl7.fhir.r4.model.SearchParameter; 019import org.hl7.fhir.r4.model.StructureDefinition; 020import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; 021import org.hl7.fhir.utilities.Utilities; 022import org.hl7.fhir.utilities.npm.NpmPackage; 023 024public class IntegrityChecker { 025 026 public class SearchParameterNodeSorter implements Comparator<SearchParameterNode> { 027 028 @Override 029 public int compare(SearchParameterNode o1, SearchParameterNode o2) { 030 return o1.name.compareTo(o2.name); 031 } 032 033 } 034 035 public class SearchParameterParamNodeSorter implements Comparator<SearchParameterParamNode> { 036 037 @Override 038 public int compare(SearchParameterParamNode o1, SearchParameterParamNode o2) { 039 return o1.sp.getCode().compareTo(o2.sp.getCode()); 040 } 041 042 } 043 044 public class SearchParameterParamNode { 045 SearchParameter sp; 046 boolean only; 047 048 public SearchParameterParamNode(SearchParameter sp, boolean only) { 049 super(); 050 this.sp = sp; 051 this.only = only; 052 } 053 054 } 055 056 public class SearchParameterNode { 057 058 private String name; 059 private List<SearchParameterParamNode> params = new ArrayList<>(); 060 061 public SearchParameterNode(String name) { 062 this.name = name; 063 } 064 065 } 066 067 public class StructureDefinitionNodeComparer implements Comparator<StructureDefinitionNode> { 068 069 @Override 070 public int compare(StructureDefinitionNode arg0, StructureDefinitionNode arg1) { 071 if (arg0.sd.getType().equals(arg1.sd.getType())) { 072 return arg0.sd.getName().compareTo(arg1.sd.getName()); 073 } else { 074 return arg0.sd.getType().compareTo(arg1.sd.getType()); 075 } 076 } 077 078 } 079 080 public class StructureDefinitionNode { 081 StructureDefinition sd; 082 List<StructureDefinitionNode> children = new ArrayList<>(); 083 084 public StructureDefinitionNode(StructureDefinition sd) { 085 this.sd = sd; 086 } 087 088 } 089 090 private NpmPackage npm; 091 092 public static void main(String[] args) throws Exception { 093 IntegrityChecker check = new IntegrityChecker(); 094 check.load(args[0]); 095 check.check(); 096 } 097 098 private void check() throws IOException { 099 dumpSD(new FileWriter("/Users/grahamegrieve/temp/r4-dump.txt")); 100// checkSD(); 101// checkSP(); 102 } 103 104 private void dumpSD(FileWriter w) throws FHIRFormatError, IOException { 105 Map<String, StructureDefinition> map = new HashMap<>(); 106 for (String sdn : npm.listResources("StructureDefinition")) { 107 InputStream s = npm.load(sdn); 108 StructureDefinition sd = (StructureDefinition) new JsonParser().parse(s); 109 map.put(sd.getUrl(), sd); 110 } 111 msg("Loaded " + map.size() + " Structures"); 112 List<String> structures = new ArrayList<>(); 113 for (StructureDefinition sd : map.values()) { 114 structures.add(sd.getUrl()); 115 } 116 Collections.sort(structures); 117 118 for (String sdn : structures) { 119 dumpSD(map.get(sdn), map, w); 120 } 121 } 122 123 private void dumpSD(StructureDefinition sd, Map<String, StructureDefinition> map, FileWriter w) throws IOException { 124 if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { 125 StructureDefinition base = sd.hasBaseDefinition() ? map.get(sd.getBaseDefinition()) : null; 126 System.out.println(sd.getType() + (base == null ? "" : " : " + base.getType())); 127 w.append(sd.getType() + (base == null ? "" : " : " + base.getType()) + "\r\n"); 128 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 129 w.append(" " + Utilities.padLeft("", ' ', Utilities.charCount(ed.getPath(), '.')) + tail(ed.getPath()) + " : " 130 + ed.typeSummary() + " [" + ed.getMin() + ".." + ed.getMax() + "]" + "\r\n"); 131 } 132 } 133 } 134 135 private String tail(String path) { 136 return path.contains(".") ? path.substring(path.lastIndexOf('.') + 1) : path; 137 } 138 139 private void checkSP() throws IOException { 140 List<SearchParameter> list = new ArrayList<>(); 141 for (String sdn : npm.listResources("SearchParameter")) { 142 InputStream s = npm.load(sdn); 143 SearchParameter sp = (SearchParameter) new JsonParser().parse(s); 144 list.add(sp); 145 } 146 msg("Loaded " + list.size() + " resources"); 147 Map<String, SearchParameterNode> map = new HashMap<>(); 148 for (SearchParameter sp : list) { 149 for (CodeType c : sp.getBase()) { 150 String s = c.primitiveValue(); 151 if (!map.containsKey(s)) { 152 map.put(s, new SearchParameterNode(s)); 153 } 154 addNode(sp, sp.getBase().size() == 1, map.get(s)); 155 } 156 } 157 for (SearchParameterNode node : sort(map.values())) { 158 dump(node); 159 } 160 } 161 162 private void dump(SearchParameterNode node) { 163 msg(node.name); 164 for (SearchParameterParamNode p : sortP(node.params)) { 165 String exp = p.sp.getExperimental() ? " **exp!" : ""; 166 if (p.only) { 167 msg(" " + p.sp.getCode() + exp); 168 } else { 169 msg(" *" + p.sp.getCode() + exp); 170 } 171 } 172 173 } 174 175 private List<SearchParameterParamNode> sortP(List<SearchParameterParamNode> params) { 176 List<SearchParameterParamNode> res = new ArrayList<>(); 177 res.addAll(params); 178 Collections.sort(res, new SearchParameterParamNodeSorter()); 179 return res; 180 } 181 182 private List<SearchParameterNode> sort(Collection<SearchParameterNode> values) { 183 List<SearchParameterNode> res = new ArrayList<>(); 184 res.addAll(values); 185 Collections.sort(res, new SearchParameterNodeSorter()); 186 return res; 187 } 188 189 private void addNode(SearchParameter sp, boolean b, SearchParameterNode node) { 190 node.params.add(new SearchParameterParamNode(sp, b)); 191 } 192 193 private void checkSD() throws IOException { 194 Map<String, StructureDefinition> map = new HashMap<>(); 195 for (String sdn : npm.listResources("StructureDefinition")) { 196 InputStream s = npm.load(sdn); 197 StructureDefinition sd = (StructureDefinition) new JsonParser().parse(s); 198 map.put(sd.getUrl(), sd); 199 } 200 msg("Loaded " + map.size() + " resources"); 201 List<StructureDefinitionNode> roots = new ArrayList<>(); 202 for (StructureDefinition sd : map.values()) { 203 if (sd.getBaseDefinition() == null || !map.containsKey(sd.getBaseDefinition())) { 204 StructureDefinitionNode root = new StructureDefinitionNode(sd); 205 roots.add(root); 206 analyse(root, map); 207 } 208 } 209 sort(roots); 210 for (StructureDefinitionNode root : roots) { 211 describe(root, 0); 212 } 213 } 214 215 private void sort(List<StructureDefinitionNode> list) { 216 Collections.sort(list, new StructureDefinitionNodeComparer()); 217 218 } 219 220 private void analyse(StructureDefinitionNode node, Map<String, StructureDefinition> map) { 221 for (StructureDefinition sd : map.values()) { 222 if (node.sd.getUrl().equals(sd.getBaseDefinition())) { 223 StructureDefinitionNode c = new StructureDefinitionNode(sd); 224 node.children.add(c); 225 analyse(c, map); 226 } 227 } 228 sort(node.children); 229 } 230 231 private void describe(StructureDefinitionNode node, int level) { 232 describe(node.sd, level); 233 for (StructureDefinitionNode c : node.children) { 234 describe(c, level + 1); 235 } 236 } 237 238 private void describe(StructureDefinition sd, int level) { 239 String exp = sd.getExperimental() ? " **exp!" : ""; 240 if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) { 241 msg(Utilities.padLeft("", ' ', level) + sd.getType() + " / " + sd.getName() + " (" + sd.getUrl() + ")" + exp); 242 } else { 243 msg(Utilities.padLeft("", ' ', level) + sd.getType() + " : " + sd.getKind() + exp); 244 } 245 } 246 247// private int analyse(Map<String, StructureDefinition> map, List<StructureDefinition> list, StructureDefinition sd) { 248// if (!list.contains(sd)) { 249// int level = 0; 250// if (sd.hasBaseDefinition()) { 251// StructureDefinition p = map.get(sd.getBaseDefinition()); 252// if (p == null) { 253// msg("Can't find parent "+sd.getBaseDefinition()+" for "+sd.getUrl()); 254// } else { 255// level = analyse(map, list, p) + 1; 256// } 257// } 258// list.add(sd); 259// sd.setUserData("level", level); 260// } 261// } 262 263 private void msg(String string) { 264 System.out.println(string); 265 } 266 267 private void load(String folder) throws IOException { 268 msg("Loading resources from " + folder); 269 npm = NpmPackage.fromFolder(folder); 270 } 271}