001package org.hl7.fhir.convertors.analytics; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.HashMap; 007import java.util.HashSet; 008import java.util.List; 009import java.util.Map; 010import java.util.Set; 011 012import javax.xml.parsers.ParserConfigurationException; 013 014import org.hl7.fhir.convertors.analytics.PackageVisitor.IPackageVisitorProcessor; 015import org.hl7.fhir.convertors.analytics.PackageVisitor.PackageContext; 016import org.hl7.fhir.exceptions.FHIRException; 017import org.hl7.fhir.exceptions.FHIRFormatError; 018import org.hl7.fhir.r5.utils.EOperationOutcome; 019import org.hl7.fhir.utilities.Utilities; 020import org.hl7.fhir.utilities.VersionUtilities; 021import org.hl7.fhir.utilities.npm.NpmPackage; 022import org.xml.sax.SAXException; 023 024public class SearchParameterAnalysis implements IPackageVisitorProcessor { 025 026 public static class SearchParameterTypeUsage { 027 private Set<String> coreUsage = new HashSet<>(); 028 private Set<String> igUsage = new HashSet<>(); 029 public String summary() { 030 return ""+coreUsage.size()+" / "+igUsage.size(); 031 } 032 } 033 034 public static class SearchParameterType { 035 private Map<String, SearchParameterTypeUsage> usages = new HashMap<>(); 036 public void seeUsage(boolean core, String usage, String url) { 037 if (!usages.containsKey(usage)) { 038 usages.put(usage, new SearchParameterTypeUsage()); 039 } 040 SearchParameterTypeUsage tu = usages.get(usage); 041 if (core) { 042 tu.coreUsage.add(url); 043 } else { 044 tu.igUsage.add(url); 045 } 046 047 } 048 } 049 050 public static class SearchParameterVersionAnalysis { 051 private Map<String, SearchParameterType> types = new HashMap<>(); 052 private String version; 053 054 public void seeUsage(boolean core, String type, String usage, String url) { 055// System.out.println("v"+version+" "+Utilities.padRight(url, ' ', 60)+" "+type+"/"+usage); 056 if (type == null) { 057 type = "n/a"; 058 } 059 if (usage == null) { 060 usage = "n/a"; 061 } 062 if (!types.containsKey(type)) { 063 types.put(type, new SearchParameterType()); 064 } 065 SearchParameterType tu = types.get(type); 066 tu.seeUsage(core, usage, url); 067 } 068 069 public void printSummary() { 070 Set<String> usages = new HashSet<>(); 071 for (SearchParameterType tu : types.values()) { 072 usages.addAll(tu.usages.keySet()); 073 } 074 List<String> ul = new ArrayList<String>(); 075 ul.addAll(usages); 076 Collections.sort(ul); 077 System.out.print(Utilities.padRight("", ' ', 10)); 078 for (String u : ul) { 079 System.out.print(Utilities.padRight(u, ' ', 10)); 080 } 081 System.out.println(); 082 for (String t : types.keySet()) { 083 System.out.print(Utilities.padRight(t, ' ', 10)); 084 SearchParameterType tu = types.get(t); 085 for (String u : ul) { 086 SearchParameterTypeUsage uu = tu.usages.get(u); 087 if (uu == null) { 088 System.out.print(Utilities.padRight("0 / 0", ' ', 10)); 089 } else { 090 System.out.print(Utilities.padRight(uu.summary(), ' ', 10)); 091 } 092 } 093 System.out.println(); 094 } 095 } 096 097 } 098 099 private Map<String, SearchParameterVersionAnalysis> versions = new HashMap<String, SearchParameterAnalysis.SearchParameterVersionAnalysis>(); 100 101 102 @Override 103 public void alreadyVisited(String pid) { 104 105 } 106 107 @Override 108 public Object startPackage(PackageContext ctxt) { 109 return null; 110 } 111 112 @Override 113 public void processResource(PackageContext ctxt, Object context, String type, String id, byte[] content) throws FHIRException, IOException, EOperationOutcome { 114// System.out.println("v"+version+" "+type+" from "+pid); 115 String pid = ctxt.getPid(); 116 String version = ctxt.getVersion(); 117 boolean core = pid.startsWith("hl7.fhir.r") && (pid.contains(".core") || pid.contains(".examples")); 118 version = VersionUtilities.getMajMin(version); 119 if (!versions.containsKey(version)) { 120 versions.put(version, new SearchParameterVersionAnalysis()); 121 versions.get(version).version = version; 122 } 123 try { 124 if (VersionUtilities.isR5Plus(version)) { 125 processR5SP(core, versions.get(version), content); 126 } else if (VersionUtilities.isR4BVer(version)) { 127 processR4SP(core, versions.get(version), content); 128 } else if (VersionUtilities.isR4Ver(version)) { 129 processR4SP(core, versions.get(version), content); 130 } else if (VersionUtilities.isR3Ver(version)) { 131 processR3SP(core, versions.get(version), content); 132 } else if (VersionUtilities.isR2Ver(version)) { 133 processR2SP(core, versions.get(version), content); 134 } 135 } catch (IOException e) { 136 throw new FHIRException(e); 137 } 138 } 139 140 @Override 141 public void finishPackage(PackageContext ctxt) { 142 143 } 144 145 private void processR5SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException { 146 org.hl7.fhir.r5.model.Resource res = new org.hl7.fhir.r5.formats.JsonParser().parse(content); 147 if (res instanceof org.hl7.fhir.r5.model.Bundle) { 148 for (org.hl7.fhir.r5.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.r5.model.Bundle) res).getEntry()) { 149 if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.r5.model.SearchParameter) { 150 org.hl7.fhir.r5.model.SearchParameter sp = (org.hl7.fhir.r5.model.SearchParameter) bnd.getResource(); 151 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getProcessingModeElement().primitiveValue(), sp.getUrl()); 152 } 153 } 154 } else { 155 org.hl7.fhir.r5.model.SearchParameter sp = (org.hl7.fhir.r5.model.SearchParameter) res; 156 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getProcessingModeElement().primitiveValue(), sp.getUrl()); 157 } 158 } 159 160 private void processR4SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException { 161 org.hl7.fhir.r4.model.Resource res = new org.hl7.fhir.r4.formats.JsonParser().parse(content); 162 if (res instanceof org.hl7.fhir.r4.model.Bundle) { 163 for (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.r4.model.Bundle) res).getEntry()) { 164 if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.r4.model.SearchParameter) { 165 org.hl7.fhir.r4.model.SearchParameter sp = (org.hl7.fhir.r4.model.SearchParameter) bnd.getResource(); 166 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl()); 167 } 168 } 169 } else { 170 org.hl7.fhir.r4.model.SearchParameter sp = (org.hl7.fhir.r4.model.SearchParameter) res; 171 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl()); 172 } 173 } 174 175 private void processR3SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException { 176 org.hl7.fhir.dstu3.model.Resource res = new org.hl7.fhir.dstu3.formats.JsonParser().parse(content); 177 if (res instanceof org.hl7.fhir.dstu3.model.Bundle) { 178 for (org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.dstu3.model.Bundle) res).getEntry()) { 179 if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.dstu3.model.SearchParameter) { 180 org.hl7.fhir.dstu3.model.SearchParameter sp = (org.hl7.fhir.dstu3.model.SearchParameter) bnd.getResource(); 181 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl()); 182 } 183 } 184 } else { 185 org.hl7.fhir.dstu3.model.SearchParameter sp = (org.hl7.fhir.dstu3.model.SearchParameter) res; 186 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl()); 187 } 188 } 189 190 private void processR2SP(boolean core, SearchParameterVersionAnalysis analysis, byte[] content) throws FHIRFormatError, IOException { 191 org.hl7.fhir.dstu2.model.Resource res = new org.hl7.fhir.dstu2.formats.JsonParser().parse(content); 192 if (res instanceof org.hl7.fhir.dstu2.model.Bundle) { 193 for (org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent bnd : ((org.hl7.fhir.dstu2.model.Bundle) res).getEntry()) { 194 if (bnd.getResource() != null && bnd.getResource() instanceof org.hl7.fhir.dstu2.model.SearchParameter) { 195 org.hl7.fhir.dstu2.model.SearchParameter sp = (org.hl7.fhir.dstu2.model.SearchParameter) bnd.getResource(); 196 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl()); 197 } 198 } 199 } else { 200 org.hl7.fhir.dstu2.model.SearchParameter sp = (org.hl7.fhir.dstu2.model.SearchParameter) res; 201 analysis.seeUsage(core, sp.getTypeElement().primitiveValue(), sp.getXpathUsageElement().primitiveValue(), sp.getUrl()); 202 } 203 } 204 205 public static void main(String[] args) throws Exception { 206 new SearchParameterAnalysis().execute(); 207 } 208 209 private void execute() throws IOException, ParserConfigurationException, SAXException, FHIRException, EOperationOutcome { 210 PackageVisitor pv = new PackageVisitor(); 211 pv.getResourceTypes().add("SearchParameter"); 212 pv.getResourceTypes().add("Bundle"); 213 pv.setOldVersions(false); 214 pv.setCorePackages(true); 215 pv.setProcessor(this); 216 pv.visitPackages(); 217 218 printSummary(); 219 } 220 221 private void printSummary() { 222 for (String v : versions.keySet()) { 223 System.out.println("-- v"+v+"---------------------"); 224 versions.get(v).printSummary(); 225 System.out.println(""); 226 } 227 228 } 229 230}