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}