001package org.hl7.fhir.r5.renderers.spreadsheets;
002
003import java.io.FileOutputStream;
004import java.io.IOException;
005import java.io.OutputStream;
006import java.util.List;
007
008import org.apache.poi.ss.usermodel.Row;
009import org.apache.poi.ss.usermodel.Sheet;
010import org.hl7.fhir.exceptions.DefinitionException;
011import org.hl7.fhir.r5.context.IWorkerContext;
012import org.hl7.fhir.r5.context.SimpleWorkerContext;
013import org.hl7.fhir.r5.model.CanonicalType;
014import org.hl7.fhir.r5.model.CodeSystem;
015import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
016import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
017import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
018import org.hl7.fhir.r5.model.ElementDefinition;
019import org.hl7.fhir.r5.model.Enumeration;
020import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
021import org.hl7.fhir.r5.model.ValueSet;
022import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
023import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
024import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
025import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
026import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
027import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent;
028import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
029import org.hl7.fhir.utilities.i18n.I18nConstants;
030
031public class CodeSystemSpreadsheetGenerator extends CanonicalSpreadsheetGenerator {
032
033  public CodeSystemSpreadsheetGenerator(IWorkerContext context) {
034    super(context);
035  }
036
037  public boolean canGenerate(CodeSystem cs) {
038    return true;
039  }
040
041  public CodeSystemSpreadsheetGenerator renderCodeSystem(CodeSystem cs) throws IOException {
042    if (cs == null) {
043      System.out.println("no code system!");
044    }
045    addCodeSystemMetadata(renderCanonicalResource(cs, false), cs);
046    
047    if (cs.hasProperty()) {
048      addProperties(cs.getProperty());
049    }
050    if (cs.hasFilter()) {
051      addFilters(cs.getFilter());
052    }
053    if (cs.hasConcept()) {
054      addConcepts(cs.getConcept());
055    }
056    return this;
057  }
058
059  private void addCodeSystemMetadata(Sheet sheet, CodeSystem cs) {
060    addMetadataRow(sheet, "Case Sensitive", cs.getCaseSensitiveElement().asStringValue());
061    addMetadataRow(sheet, "Value Set (all codes)", cs.getValueSet());
062    addMetadataRow(sheet, "Hierarchy", cs.getHierarchyMeaningElement().asStringValue());
063    addMetadataRow(sheet, "Compositional", cs.getCompositionalElement().asStringValue());
064    addMetadataRow(sheet, "Version Needed?", cs.getVersionNeededElement().asStringValue());
065    addMetadataRow(sheet, "Content", cs.getContentElement().asStringValue());
066    addMetadataRow(sheet, "Supplements", cs.getSupplements());
067    addMetadataRow(sheet, "Count", cs.getCountElement().asStringValue());
068  }
069  
070  private void addFilters(List<CodeSystemFilterComponent> filters) {
071    Sheet sheet = makeSheet("Filters");
072    addHeaders(sheet, "Code", "Description", "Operators", "Value");
073    for (CodeSystemFilterComponent f : filters) {
074      CommaSeparatedStringBuilder cs = new CommaSeparatedStringBuilder();
075      for (Enumeration<FilterOperator> op : f.getOperator()) {
076        cs.append(op.asStringValue());
077      }
078      addRow(sheet, f.getCode(), f.getDescription(), cs.toString(), f.getValue());
079    }        
080  }
081
082  private void addProperties(List<PropertyComponent> properties) {
083    Sheet sheet = makeSheet("Properties");
084    addHeaders(sheet, "Code", "Uri", "Description", "Type");
085    for (PropertyComponent p : properties) {
086      addRow(sheet, p.getCode(), p.getUri(), p.getDescription(), p.getTypeElement().asStringValue());
087    }    
088  }
089
090  private void addConcepts(List<ConceptDefinitionComponent> concepts) {
091    Sheet sheet = makeSheet("Concepts");
092    addHeaders(sheet, "Level", "Code", "Display", "Definition"); //todo: properties and designations
093    addConcepts(sheet, 1, concepts);    
094  }
095
096  private void addConcepts(Sheet sheet, int i, List<ConceptDefinitionComponent> concepts) {
097    for (ConceptDefinitionComponent c : concepts) {
098      addRow(sheet, Integer.toString(i), c.getCode(), c.getDisplay(), c.getDefinition());
099      if (c.hasConcept()) {
100        addConcepts(sheet, i+1, c.getConcept());
101      }
102    }    
103  }
104
105  private void genExpansionParams(List<ValueSetExpansionParameterComponent> params) {
106    Sheet sheet = makeSheet("Expansion Parameters");
107    addHeaders(sheet, "Parameter", "Value");
108    for (ValueSetExpansionParameterComponent p : params) {
109      addRow(sheet, p.getName(), dr.display(p.getValue()));          
110    }    
111  }
112
113  private void genExpansion(List<ValueSetExpansionContainsComponent> list) {
114    Sheet sheet = makeSheet("Expansion");
115    addHeaders(sheet, "Level", "System", "version", "Code", "Display", "Abstract", "Inactive");
116    genExpansionEntry(1, list, sheet);    
117  }
118
119  public void genExpansionEntry(int level, List<ValueSetExpansionContainsComponent> list, Sheet sheet) {
120    for (ValueSetExpansionContainsComponent p : list) {
121      addRow(sheet, Integer.toString(level), p.getSystem(), p.getVersion(), p.getCode(), p.getDisplay(), bool(p.getAbstract()), bool(p.getInactive()));  
122      if (p.hasContains()) {
123        genExpansionEntry(level + 1, p.getContains(), sheet);
124      }
125    }
126  }
127  
128  private String bool(boolean value) {    
129    return value ? "" : "false";
130  }
131
132//  private void genInclude(ValueSet vs, ConceptSetComponent inc, String mode) {
133//    if (inc.hasSystem()) {
134//      genIncludeSystem(vs, inc, mode);
135//    } else {
136//      genIncludeValueSets(vs, inc, mode);      
137//    }
138//    String subname = inc.hasSystem() ?  : "ValueSets";
139//    
140//
141//  Row headerRow = sheet.createRow(0);
142//  for (int i = 0; i < titles.length; i++) {
143//    addCell(headerRow, i, titles[i], styles.get("header"));
144//  }
145//  int i = titles.length - 1;
146//  for (StructureDefinitionMappingComponent map : sd.getMapping()) {
147//    i++;
148//    addCell(headerRow, i, "Mapping: " + map.getName(), styles.get("header"));
149//  }    
150//
151//  for (ElementDefinition child : sd.getSnapshot().getElement()) {
152//    processElement(sheet, sd, child);
153//  }
154//  configureSheet(sheet, sd);
155//  }
156
157//  private void genIncludeValueSets(ValueSet vs, ConceptSetComponent inc, String mode) {
158//    Sheet sheet = makeSheet(mode+" ValueSets");
159//    addValueSets(sheet, inc.getValueSet()); 
160//    configureSheet(sheet);
161//  }
162//
163//  private void genIncludeSystem(ValueSet vs, ConceptSetComponent inc, String mode) {
164//    Sheet sheet = makeSheet(mode+" from "+dr.displaySystem(inc.getSystem()));
165//    if (inc.hasValueSet()) {
166//      addValueSets(sheet, inc.getValueSet());
167//    }
168//    if (inc.hasFilter()) {
169//      addFilters(sheet, inc.getFilter());
170//    }
171//    if (inc.hasConcept()) {
172//      addConcepts(sheet, inc.getConcept());
173//    }
174//    if (!inc.hasConcept() && !inc.hasFilter()) {
175//      addAllCodes(sheet);
176//    }
177//    addRow(sheet, "", "");          
178//    addRow(sheet, "System URI", inc.getSystem());          
179//    
180//    configureSheet(sheet);
181//  }
182
183  private void addAllCodes(Sheet sheet) {
184    addHeaders(sheet, "Codes");     
185    addRow(sheet, "All codes");          
186  }
187
188  private void addValueSets(Sheet sheet, List<CanonicalType> valueSets) {
189    addHeaders(sheet, "ValueSet URL");
190    for (CanonicalType u : valueSets) {
191      addRow(sheet, u.getValue());
192    }
193  }
194
195  private void configureSheet(Sheet sheet) {
196    sheet.setColumnWidth(0, columnPixels(30));
197    sheet.setColumnWidth(1, columnPixels(40));
198    sheet.setColumnWidth(1, columnPixels(50));
199  }
200
201  private void addConcepts(Sheet sheet, List<ConceptReferenceComponent> concepts) {
202    addHeaders(sheet, "Concept", "Description"); // todo: designations    
203    for (ConceptReferenceComponent cd : concepts) {
204      addRow(sheet, cd.getCode(), cd.getDisplay());      
205    }    
206  }
207
208  private void addFilters(Sheet sheet, List<ConceptSetFilterComponent> filters) {
209    addHeaders(sheet, "Property", "Operation", "Value");    
210    for (ConceptSetFilterComponent f : filters) {
211      addRow(sheet, f.getProperty(), f.getOpElement().asStringValue(), f.getValue());
212    }
213  }
214
215}