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 if (ed.getBinding().getValueSet() instanceof CanonicalType)
162      line.addString(ed.getBinding().getValueSetCanonicalType().getValue());
163      else
164      line.addString(ed.getBinding().getValueSetUriType().getValue());
165    } else {
166      line.addValue("");
167      line.addValue("");
168      line.addValue("");
169    }
170    line.addString(itemList(ed.getCode()));
171    if (ed.hasSlicing()) {
172      line.addString(itemList(ed.getSlicing().getDiscriminator()));
173      line.addString(ed.getSlicing().getDescription());
174      line.addBoolean(ed.getSlicing().getOrdered());
175      line.addString(ed.getSlicing().getRules()!=null ? ed.getSlicing().getRules().toCode() : "");
176    } else {
177      line.addValue("");
178      line.addValue("");
179      line.addValue("");      
180    }
181    if (ed.getBase()!=null) {
182      line.addString(ed.getBase().getPath());
183      line.addValue(ed.getBase().getMin());
184      line.addValue(ed.getBase().getMax());
185    } else {
186      line.addValue("");
187      line.addValue("");
188      line.addValue("");      
189    }
190    line.addString(itemList(ed.getCondition()));
191    line.addString(itemList(ed.getConstraint()));
192    for (StructureDefinitionMappingComponent mapKey : def.getMapping()) {
193      for (ElementDefinitionMappingComponent map : ed.getMapping()) {
194        if (map.getIdentity().equals(mapKey.getIdentity()))
195                line.addString(map.getMap());
196      }
197    }
198  }
199
200
201  private String itemList(List l) {
202    StringBuilder s = new StringBuilder();
203    for (int i =0; i< l.size(); i++) {
204      Object o = l.get(i);
205      String val = "";
206      if (o instanceof StringType) {
207        val = ((StringType)o).getValue();
208      } else if (o instanceof UriType) {
209        val = ((UriType)o).getValue();
210      } else if (o instanceof IdType) {
211        val = ((IdType)o).getValue();
212      } else if (o instanceof Enumeration<?>) {
213        val = o.toString();
214      } else if (o instanceof TypeRefComponent) {
215        TypeRefComponent t = (TypeRefComponent)o;
216          val = t.getCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}")  + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
217      } else if (o instanceof Coding) {
218        Coding t = (Coding)o;
219        val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")");
220      } else if (o instanceof ElementDefinitionConstraintComponent) {
221        ElementDefinitionConstraintComponent c = (ElementDefinitionConstraintComponent)o;
222        val = c.getKey() + ":" + c.getHuman() + " {" + c.getExpression() + "}";
223      } else if (o instanceof ElementDefinitionSlicingDiscriminatorComponent) {
224        ElementDefinitionSlicingDiscriminatorComponent c = (ElementDefinitionSlicingDiscriminatorComponent)o;
225        val = c.getType().toCode() + ":" + c.getPath() + "}";
226        
227      } else {
228        val = o.toString();
229        val = val.substring(val.indexOf("[")+1);
230        val = val.substring(0, val.indexOf("]"));
231      }
232      s = s.append(val);
233      if (i == 0)
234        s.append("\n");
235    }
236    return s.toString();
237  }
238  
239  private String renderType(Type value) throws Exception {
240    String s = null;
241    ByteArrayOutputStream bs = new ByteArrayOutputStream();
242    if (asXml) {
243      xml.setOutputStyle(OutputStyle.PRETTY);
244      xml.compose(bs, "", value);
245      bs.close();
246      s = bs.toString();
247      s = s.substring(s.indexOf("\n")+2);
248    } else {
249      json.setOutputStyle(OutputStyle.PRETTY);
250      json.compose(bs, value, "");
251      bs.close();
252      s = bs.toString();
253        }
254    return s;
255  }
256
257  public void dump() throws IOException {
258    for (CSVLine l : lines)
259      ln(l.toString());
260    
261    flush();
262    close();
263  }
264
265}