001package org.hl7.fhir.r4.utils.formats;
002
003import java.io.ByteArrayOutputStream;
004import java.io.IOException;
005import java.io.OutputStream;
006import java.io.UnsupportedEncodingException;
007import java.util.ArrayList;
008import java.util.Enumeration;
009import java.util.List;
010
011import org.hl7.fhir.r4.formats.IParser.OutputStyle;
012import org.hl7.fhir.r4.formats.JsonParser;
013import org.hl7.fhir.r4.formats.XmlParser;
014import org.hl7.fhir.r4.model.CanonicalType;
015import org.hl7.fhir.r4.model.Coding;
016import org.hl7.fhir.r4.model.ElementDefinition;
017import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionConstraintComponent;
018import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionMappingComponent;
019import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent;
020import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
021import org.hl7.fhir.r4.model.IdType;
022import org.hl7.fhir.r4.model.Reference;
023import org.hl7.fhir.r4.model.StringType;
024import org.hl7.fhir.r4.model.StructureDefinition;
025import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionMappingComponent;
026import org.hl7.fhir.r4.model.Type;
027import org.hl7.fhir.r4.model.UriType;
028import org.hl7.fhir.utilities.TextStreamWriter;
029
030
031public class CSVWriter  extends TextStreamWriter  {
032
033  private StructureDefinition def;
034  private List<StructureDefinitionMappingComponent> mapKeys = new ArrayList<StructureDefinitionMappingComponent>();
035  private List<CSVLine> lines = new ArrayList<CSVLine>();
036  private XmlParser xml = new XmlParser();
037  private JsonParser json = new JsonParser();
038  private boolean asXml;
039
040  private class CSVLine {
041    private String line = "";
042    
043    public void addString(String s) {
044      line = line + (line.equals("") ? "":",") + "\"" + csvEscape(s) + "\"";
045    }
046    
047    public void addString(StringType s) {
048      addString(s==null? "" : s.getValue());
049    }
050
051    public void addValue(String s) {
052      line = line + (line.equals("") ? "":",") + s;
053    }
054    
055    public void addValue(int s) {
056      line = line + (line.equals("") ? "":",") + s;
057    }
058
059    public void addBoolean(boolean b) {
060      addValue(b ? "Y" : "");
061    }
062
063    protected String csvEscape(String s) {
064      if (s==null)
065        return "";
066      else if (s.contains("\""))
067          return s.substring(0,s.indexOf("\"")) + "\"\"" + csvEscape(s.substring(s.indexOf("\"")+1));
068      else
069        return s;
070    }
071    
072    public String toString() {
073      return line;
074    }
075  }
076
077  public CSVWriter(OutputStream out, StructureDefinition def, boolean asXml) throws UnsupportedEncodingException {
078    super(out);
079    this.asXml = asXml;
080    this.def = def;
081    CSVLine header = new CSVLine();
082    lines.add(header);
083    header.addString("Path");                 //A
084    header.addString("Slice Name");           //B
085    header.addString("Alias(s)");             //C
086    header.addString("Label");                //D
087    header.addString("Min");                  //E
088    header.addString("Max");                  //F
089    header.addString("Must Support?");        //G
090    header.addString("Is Modifier?");         //H
091    header.addString("Is Summary?");          //I
092    header.addString("Type(s)");              //J
093    header.addString("Short");                //K
094    header.addString("Definition");           //L
095    header.addString("Comments");             //M
096    header.addString("Requirements");         //N
097    header.addString("Default Value");        //O
098    header.addString("Meaning When Missing"); //P
099    header.addString("Fixed Value");          //Q
100    header.addString("Pattern");              //R
101    header.addString("Example");              //S
102    header.addString("Minimum Value");        //T
103    header.addString("Maximum Value");        //U
104    header.addString("Maximum Length");       //V
105    header.addString("Binding Strength");     //W
106    header.addString("Binding Description");  //X
107    header.addString("Binding Value Set");    //Y
108    header.addString("Code");                 //Z
109    header.addString("Slicing Discriminator");//AA
110    header.addString("Slicing Description");  //AB
111    header.addString("Slicing Ordered");      //AC
112    header.addString("Slicing Rules");        //AD
113    header.addString("Base Path");            //AE
114    header.addString("Base Min");             //AF
115    header.addString("Base Max");             //AG
116    header.addString("Condition(s)");         //AH
117    header.addString("Constraint(s)");        //AI
118    for (StructureDefinitionMappingComponent map : def.getMapping()) {
119      header.addString("Mapping: " + map.getName());
120    }
121  }
122  
123/*  private void findMapKeys(StructureDefinition def, List<StructureDefinitionMappingComponent> maps, IWorkerContext context) {
124        maps.addAll(def.getMapping());
125        if (def.getBaseDefinition()!=null) {
126          StructureDefinition base = context.fetchResource(StructureDefinition.class, def.getBaseDefinition());
127          findMapKeys(base, maps, context);
128        }
129  }*/
130
131  public void processElement(ElementDefinition ed) throws Exception {
132    CSVLine line = new CSVLine();
133    lines.add(line);
134    line.addString(ed.getPath());
135    line.addString(ed.getSliceName());
136    line.addString(itemList(ed.getAlias()));
137    line.addString(ed.getLabel());
138    line.addValue(ed.getMin());
139    line.addValue(ed.getMax());
140    line.addString(ed.getMustSupport() ? "Y" : "");
141    line.addString(ed.getIsModifier() ? "Y" : "");
142    line.addString(ed.getIsSummary() ? "Y" : "");
143    line.addString(itemList(ed.getType()));
144    line.addString(ed.getShort());
145    line.addString(ed.getDefinition());
146    line.addString(ed.getComment());
147    line.addString(ed.getRequirements());
148    line.addString(ed.getDefaultValue()!=null ? renderType(ed.getDefaultValue()) : "");
149    line.addString(ed.getMeaningWhenMissing());
150    line.addString(ed.hasFixed() ? renderType(ed.getFixed()) : "");
151    line.addString(ed.hasPattern() ? renderType(ed.getPattern()) : "");
152    line.addString(ed.hasExample() ? renderType(ed.getExample().get(0).getValue()) : ""); // todo...?
153    line.addString(ed.hasMinValue() ? renderType(ed.getMinValue()) : "");
154    line.addString(ed.hasMaxValue() ? renderType(ed.getMaxValue()) : "");
155    line.addValue((ed.hasMaxLength() ? Integer.toString(ed.getMaxLength()) : ""));
156    if (ed.hasBinding()) {
157      line.addString(ed.getBinding().getStrength()!=null ? ed.getBinding().getStrength().toCode() : "");
158      line.addString(ed.getBinding().getDescription());
159      if (ed.getBinding().getValueSet()==null)
160        line.addString("");
161      else
162        line.addString(ed.getBinding().getValueSet());
163    } else {
164      line.addValue("");
165      line.addValue("");
166      line.addValue("");
167    }
168    line.addString(itemList(ed.getCode()));
169    if (ed.hasSlicing()) {
170      line.addString(itemList(ed.getSlicing().getDiscriminator()));
171      line.addString(ed.getSlicing().getDescription());
172      line.addBoolean(ed.getSlicing().getOrdered());
173      line.addString(ed.getSlicing().getRules()!=null ? ed.getSlicing().getRules().toCode() : "");
174    } else {
175      line.addValue("");
176      line.addValue("");
177      line.addValue("");      
178    }
179    if (ed.getBase()!=null) {
180      line.addString(ed.getBase().getPath());
181      line.addValue(ed.getBase().getMin());
182      line.addValue(ed.getBase().getMax());
183    } else {
184      line.addValue("");
185      line.addValue("");
186      line.addValue("");      
187    }
188    line.addString(itemList(ed.getCondition()));
189    line.addString(itemList(ed.getConstraint()));
190    for (StructureDefinitionMappingComponent mapKey : def.getMapping()) {
191      for (ElementDefinitionMappingComponent map : ed.getMapping()) {
192        if (map.getIdentity().equals(mapKey.getIdentity()))
193                line.addString(map.getMap());
194      }
195    }
196  }
197
198
199  private String itemList(List l) {
200    StringBuilder s = new StringBuilder();
201    for (int i =0; i< l.size(); i++) {
202      Object o = l.get(i);
203      String val = "";
204      if (o instanceof StringType) {
205        val = ((StringType)o).getValue();
206      } else if (o instanceof UriType) {
207        val = ((UriType)o).getValue();
208      } else if (o instanceof IdType) {
209        val = ((IdType)o).getValue();
210      } else if (o instanceof Enumeration<?>) {
211        val = o.toString();
212      } else if (o instanceof TypeRefComponent) {
213        TypeRefComponent t = (TypeRefComponent)o;
214          val = t.getCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}")  + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
215      } else if (o instanceof Coding) {
216        Coding t = (Coding)o;
217        val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")");
218      } else if (o instanceof ElementDefinitionConstraintComponent) {
219        ElementDefinitionConstraintComponent c = (ElementDefinitionConstraintComponent)o;
220        val = c.getKey() + ":" + c.getHuman() + " {" + c.getExpression() + "}";
221      } else if (o instanceof ElementDefinitionSlicingDiscriminatorComponent) {
222        ElementDefinitionSlicingDiscriminatorComponent c = (ElementDefinitionSlicingDiscriminatorComponent)o;
223        val = c.getType().toCode() + ":" + c.getPath() + "}";
224        
225      } else {
226        val = o.toString();
227        val = val.substring(val.indexOf("[")+1);
228        val = val.substring(0, val.indexOf("]"));
229      }
230      s = s.append(val);
231      if (i == 0)
232        s.append("\n");
233    }
234    return s.toString();
235  }
236  
237  private String renderType(Type value) throws Exception {
238    String s = null;
239    ByteArrayOutputStream bs = new ByteArrayOutputStream();
240    if (asXml) {
241      xml.setOutputStyle(OutputStyle.PRETTY);
242      xml.compose(bs, "", value);
243      bs.close();
244      s = bs.toString();
245      s = s.substring(s.indexOf("\n")+2);
246    } else {
247      json.setOutputStyle(OutputStyle.PRETTY);
248      json.compose(bs, value, "");
249      bs.close();
250      s = bs.toString();
251        }
252    return s;
253  }
254
255  public void dump() throws IOException {
256    for (CSVLine l : lines)
257      ln(l.toString());
258    
259    flush();
260    close();
261  }
262
263}