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