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