001package org.hl7.fhir.r5.utils.formats;
002
003/*
004  Copyright (c) 2011+, HL7, Inc.
005  All rights reserved.
006  
007  Redistribution and use in source and binary forms, with or without modification, 
008  are permitted provided that the following conditions are met:
009    
010   * Redistributions of source code must retain the above copyright notice, this 
011     list of conditions and the following disclaimer.
012   * Redistributions in binary form must reproduce the above copyright notice, 
013     this list of conditions and the following disclaimer in the documentation 
014     and/or other materials provided with the distribution.
015   * Neither the name of HL7 nor the names of its contributors may be used to 
016     endorse or promote products derived from this software without specific 
017     prior written permission.
018  
019  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
020  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
021  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
022  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
023  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
024  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
025  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
026  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
027  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
028  POSSIBILITY OF SUCH DAMAGE.
029  
030 */
031
032
033
034import java.io.ByteArrayOutputStream;
035import java.io.IOException;
036import java.io.OutputStream;
037import java.io.UnsupportedEncodingException;
038import java.util.ArrayList;
039import java.util.Enumeration;
040import java.util.List;
041
042import org.hl7.fhir.r5.formats.IParser.OutputStyle;
043import org.hl7.fhir.r5.formats.JsonParser;
044import org.hl7.fhir.r5.formats.XmlParser;
045import org.hl7.fhir.r5.model.Coding;
046import org.hl7.fhir.r5.model.DataType;
047import org.hl7.fhir.r5.model.ElementDefinition;
048import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent;
049import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent;
050import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent;
051import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
052import org.hl7.fhir.r5.model.IdType;
053import org.hl7.fhir.r5.model.StringType;
054import org.hl7.fhir.r5.model.StructureDefinition;
055import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent;
056import org.hl7.fhir.r5.model.UriType;
057import org.hl7.fhir.utilities.TextStreamWriter;
058
059
060public class CSVWriter  extends TextStreamWriter  {
061
062  private StructureDefinition def;
063  private List<StructureDefinitionMappingComponent> mapKeys = new ArrayList<StructureDefinitionMappingComponent>();
064  private List<CSVLine> lines = new ArrayList<CSVLine>();
065  private XmlParser xml = new XmlParser();
066  private JsonParser json = new JsonParser();
067  private boolean asXml;
068
069  private class CSVLine {
070    private String line = "";
071    
072    public void addString(String s) {
073      line = line + (line.equals("") ? "":",") + "\"" + csvEscape(s) + "\"";
074    }
075    
076    public void addString(StringType s) {
077      addString(s==null? "" : s.getValue());
078    }
079
080    public void addValue(String s) {
081      line = line + (line.equals("") ? "":",") + s;
082    }
083    
084    public void addValue(int s) {
085      line = line + (line.equals("") ? "":",") + s;
086    }
087
088    public void addBoolean(boolean b) {
089      addValue(b ? "Y" : "");
090    }
091
092    protected String csvEscape(String s) {
093      if (s==null)
094        return "";
095      else if (s.contains("\""))
096          return s.substring(0,s.indexOf("\"")) + "\"\"" + csvEscape(s.substring(s.indexOf("\"")+1));
097      else
098        return s;
099    }
100    
101    public String toString() {
102      return line;
103    }
104  }
105
106  public CSVWriter(OutputStream out, StructureDefinition def, boolean asXml) throws UnsupportedEncodingException {
107    super(out);
108    this.asXml = asXml;
109    this.def = def;
110    CSVLine header = new CSVLine();
111    lines.add(header);
112    header.addString("Id");                   // 
113    header.addString("Path");                 //A
114    header.addString("Slice Name");           //B
115    header.addString("Alias(s)");             //C
116    header.addString("Label");                //D
117    header.addString("Min");                  //E
118    header.addString("Max");                  //F
119    header.addString("Must Support?");        //G
120    header.addString("Is Modifier?");         //H
121    header.addString("Is Summary?");          //I
122    header.addString("Type(s)");              //J
123    header.addString("Short");                //K
124    header.addString("Definition");           //L
125    header.addString("Comments");             //M
126    header.addString("Requirements");         //N
127    header.addString("Default Value");        //O
128    header.addString("Meaning When Missing"); //P
129    header.addString("Fixed Value");          //Q
130    header.addString("Pattern");              //R
131    header.addString("Example");              //S
132    header.addString("Minimum Value");        //T
133    header.addString("Maximum Value");        //U
134    header.addString("Maximum Length");       //V
135    header.addString("Binding Strength");     //W
136    header.addString("Binding Description");  //X
137    header.addString("Binding Value Set");    //Y
138    header.addString("Code");                 //Z
139    header.addString("Slicing Discriminator");//AA
140    header.addString("Slicing Description");  //AB
141    header.addString("Slicing Ordered");      //AC
142    header.addString("Slicing Rules");        //AD
143    header.addString("Base Path");            //AE
144    header.addString("Base Min");             //AF
145    header.addString("Base Max");             //AG
146    header.addString("Condition(s)");         //AH
147    header.addString("Constraint(s)");        //AI
148    for (StructureDefinitionMappingComponent map : def.getMapping()) {
149      header.addString("Mapping: " + map.getName());
150    }
151  }
152  
153  public CSVWriter(OutputStream out, boolean asXml) throws UnsupportedEncodingException {
154    super(out);
155    this.asXml = asXml;
156    this.def = null;
157    CSVLine header = new CSVLine();
158    lines.add(header);
159    header.addString("Profile");              // 
160    header.addString("Id");                   // 
161    header.addString("Path");                 //A
162    header.addString("Slice Name");           //B
163    header.addString("Alias(s)");             //C
164    header.addString("Label");                //D
165    header.addString("Min");                  //E
166    header.addString("Max");                  //F
167    header.addString("Must Support?");        //G
168    header.addString("Is Modifier?");         //H
169    header.addString("Is Summary?");          //I
170    header.addString("Type(s)");              //J
171    header.addString("Short");                //K
172    header.addString("Definition");           //L
173    header.addString("Comments");             //M
174    header.addString("Requirements");         //N
175    header.addString("Default Value");        //O
176    header.addString("Meaning When Missing"); //P
177    header.addString("Fixed Value");          //Q
178    header.addString("Pattern");              //R
179    header.addString("Example");              //S
180    header.addString("Minimum Value");        //T
181    header.addString("Maximum Value");        //U
182    header.addString("Maximum Length");       //V
183    header.addString("Binding Strength");     //W
184    header.addString("Binding Description");  //X
185    header.addString("Binding Value Set");    //Y
186    header.addString("Code");                 //Z
187    header.addString("Slicing Discriminator");//AA
188    header.addString("Slicing Description");  //AB
189    header.addString("Slicing Ordered");      //AC
190    header.addString("Slicing Rules");        //AD
191    header.addString("Base Path");            //AE
192    header.addString("Base Min");             //AF
193    header.addString("Base Max");             //AG
194    header.addString("Condition(s)");         //AH
195    header.addString("Constraint(s)");        //AI
196  }
197  
198/*  private void findMapKeys(StructureDefinition def, List<StructureDefinitionMappingComponent> maps, IWorkerContext context) {
199        maps.addAll(def.getMapping());
200        if (def.getBaseDefinition()!=null) {
201          StructureDefinition base = context.fetchResource(StructureDefinition.class, def.getBaseDefinition());
202          findMapKeys(base, maps, context);
203        }
204  }*/
205
206  public void processElement(StructureDefinition sd, ElementDefinition ed) throws Exception {
207    CSVLine line = new CSVLine();
208    lines.add(line);
209    if (def == null) {
210      line.addString(sd.getId());      
211    }
212    line.addString(ed.getId());
213    line.addString(ed.getPath());
214    line.addString(ed.getSliceName());
215    line.addString(itemList(ed.getAlias()));
216    line.addString(ed.getLabel());
217    line.addValue(ed.getMin());
218    line.addValue(ed.getMax());
219    line.addString(ed.getMustSupport() ? "Y" : "");
220    line.addString(ed.getIsModifier() ? "Y" : "");
221    line.addString(ed.getIsSummary() ? "Y" : "");
222    line.addString(itemList(ed.getType()));
223    line.addString(ed.getShort());
224    line.addString(ed.getDefinition());
225    line.addString(ed.getComment());
226    line.addString(ed.getRequirements());
227    line.addString(ed.getDefaultValue()!=null ? renderType(ed.getDefaultValue()) : "");
228    line.addString(ed.getMeaningWhenMissing());
229    line.addString(ed.hasFixed() ? renderType(ed.getFixed()) : "");
230    line.addString(ed.hasPattern() ? renderType(ed.getPattern()) : "");
231    line.addString(ed.hasExample() ? renderType(ed.getExample().get(0).getValue()) : ""); // todo...?
232    line.addString(ed.hasMinValue() ? renderType(ed.getMinValue()) : "");
233    line.addString(ed.hasMaxValue() ? renderType(ed.getMaxValue()) : "");
234    line.addValue((ed.hasMaxLength() ? Integer.toString(ed.getMaxLength()) : ""));
235    if (ed.hasBinding()) {
236      line.addString(ed.getBinding().getStrength()!=null ? ed.getBinding().getStrength().toCode() : "");
237      line.addString(ed.getBinding().getDescription());
238      if (ed.getBinding().getValueSet()==null)
239        line.addString("");
240      else
241        line.addString(ed.getBinding().getValueSet());
242    } else {
243      line.addValue("");
244      line.addValue("");
245      line.addValue("");
246    }
247    line.addString(itemList(ed.getCode()));
248    if (ed.hasSlicing()) {
249      line.addString(itemList(ed.getSlicing().getDiscriminator()));
250      line.addString(ed.getSlicing().getDescription());
251      line.addBoolean(ed.getSlicing().getOrdered());
252      line.addString(ed.getSlicing().getRules()!=null ? ed.getSlicing().getRules().toCode() : "");
253    } else {
254      line.addValue("");
255      line.addValue("");
256      line.addValue("");      
257    }
258    if (ed.getBase()!=null) {
259      line.addString(ed.getBase().getPath());
260      line.addValue(ed.getBase().getMin());
261      line.addValue(ed.getBase().getMax());
262    } else {
263      line.addValue("");
264      line.addValue("");
265      line.addValue("");      
266    }
267    line.addString(itemList(ed.getCondition()));
268    line.addString(itemList(ed.getConstraint()));
269    if (def != null) {
270      for (StructureDefinitionMappingComponent mapKey : def.getMapping()) {
271        for (ElementDefinitionMappingComponent map : ed.getMapping()) {
272          if (map.getIdentity().equals(mapKey.getIdentity()))
273            line.addString(map.getMap());
274        }
275      }
276    }
277  }
278
279
280  private String itemList(List l) {
281    StringBuilder s = new StringBuilder();
282    for (int i =0; i< l.size(); i++) {
283      Object o = l.get(i);
284      String val = "";
285      if (o instanceof StringType) {
286        val = ((StringType)o).getValue();
287      } else if (o instanceof UriType) {
288        val = ((UriType)o).getValue();
289      } else if (o instanceof IdType) {
290        val = ((IdType)o).getValue();
291      } else if (o instanceof Enumeration<?>) {
292        val = o.toString();
293      } else if (o instanceof TypeRefComponent) {
294        TypeRefComponent t = (TypeRefComponent)o;
295          val = t.getWorkingCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}")  + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
296      } else if (o instanceof Coding) {
297        Coding t = (Coding)o;
298        val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")");
299      } else if (o instanceof ElementDefinitionConstraintComponent) {
300        ElementDefinitionConstraintComponent c = (ElementDefinitionConstraintComponent)o;
301        val = c.getKey() + ":" + c.getHuman() + " {" + c.getExpression() + "}";
302      } else if (o instanceof ElementDefinitionSlicingDiscriminatorComponent) {
303        ElementDefinitionSlicingDiscriminatorComponent c = (ElementDefinitionSlicingDiscriminatorComponent)o;
304        val = c.getType().toCode() + ":" + c.getPath() + "}";
305        
306      } else {
307        val = o.toString();
308        val = val.substring(val.indexOf("[")+1);
309        val = val.substring(0, val.indexOf("]"));
310      }
311      s = s.append(val);
312      if (i == 0)
313        s.append("\n");
314    }
315    return s.toString();
316  }
317  
318  private String renderType(DataType value) throws Exception {
319    String s = null;
320    ByteArrayOutputStream bs = new ByteArrayOutputStream();
321    if (asXml) {
322      xml.setOutputStyle(OutputStyle.PRETTY);
323      xml.compose(bs, "", value);
324      bs.close();
325      s = bs.toString();
326      s = s.substring(s.indexOf("\n")+2);
327    } else {
328      json.setOutputStyle(OutputStyle.PRETTY);
329      json.compose(bs, value, "");
330      bs.close();
331      s = bs.toString();
332        }
333    return s;
334  }
335
336  public void dump() throws IOException {
337    for (CSVLine l : lines)
338      ln(l.toString());
339    
340    flush();
341    close();
342  }
343
344}