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