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("Path");                 //A
113    header.addString("Slice Name");           //B
114    header.addString("Alias(s)");             //C
115    header.addString("Label");                //D
116    header.addString("Min");                  //E
117    header.addString("Max");                  //F
118    header.addString("Must Support?");        //G
119    header.addString("Is Modifier?");         //H
120    header.addString("Is Summary?");          //I
121    header.addString("Type(s)");              //J
122    header.addString("Short");                //K
123    header.addString("Definition");           //L
124    header.addString("Comments");             //M
125    header.addString("Requirements");         //N
126    header.addString("Default Value");        //O
127    header.addString("Meaning When Missing"); //P
128    header.addString("Fixed Value");          //Q
129    header.addString("Pattern");              //R
130    header.addString("Example");              //S
131    header.addString("Minimum Value");        //T
132    header.addString("Maximum Value");        //U
133    header.addString("Maximum Length");       //V
134    header.addString("Binding Strength");     //W
135    header.addString("Binding Description");  //X
136    header.addString("Binding Value Set");    //Y
137    header.addString("Code");                 //Z
138    header.addString("Slicing Discriminator");//AA
139    header.addString("Slicing Description");  //AB
140    header.addString("Slicing Ordered");      //AC
141    header.addString("Slicing Rules");        //AD
142    header.addString("Base Path");            //AE
143    header.addString("Base Min");             //AF
144    header.addString("Base Max");             //AG
145    header.addString("Condition(s)");         //AH
146    header.addString("Constraint(s)");        //AI
147    for (StructureDefinitionMappingComponent map : def.getMapping()) {
148      header.addString("Mapping: " + map.getName());
149    }
150  }
151  
152/*  private void findMapKeys(StructureDefinition def, List<StructureDefinitionMappingComponent> maps, IWorkerContext context) {
153        maps.addAll(def.getMapping());
154        if (def.getBaseDefinition()!=null) {
155          StructureDefinition base = context.fetchResource(StructureDefinition.class, def.getBaseDefinition());
156          findMapKeys(base, maps, context);
157        }
158  }*/
159
160  public void processElement(ElementDefinition ed) throws Exception {
161    CSVLine line = new CSVLine();
162    lines.add(line);
163    line.addString(ed.getPath());
164    line.addString(ed.getSliceName());
165    line.addString(itemList(ed.getAlias()));
166    line.addString(ed.getLabel());
167    line.addValue(ed.getMin());
168    line.addValue(ed.getMax());
169    line.addString(ed.getMustSupport() ? "Y" : "");
170    line.addString(ed.getIsModifier() ? "Y" : "");
171    line.addString(ed.getIsSummary() ? "Y" : "");
172    line.addString(itemList(ed.getType()));
173    line.addString(ed.getShort());
174    line.addString(ed.getDefinition());
175    line.addString(ed.getComment());
176    line.addString(ed.getRequirements());
177    line.addString(ed.getDefaultValue()!=null ? renderType(ed.getDefaultValue()) : "");
178    line.addString(ed.getMeaningWhenMissing());
179    line.addString(ed.hasFixed() ? renderType(ed.getFixed()) : "");
180    line.addString(ed.hasPattern() ? renderType(ed.getPattern()) : "");
181    line.addString(ed.hasExample() ? renderType(ed.getExample().get(0).getValue()) : ""); // todo...?
182    line.addString(ed.hasMinValue() ? renderType(ed.getMinValue()) : "");
183    line.addString(ed.hasMaxValue() ? renderType(ed.getMaxValue()) : "");
184    line.addValue((ed.hasMaxLength() ? Integer.toString(ed.getMaxLength()) : ""));
185    if (ed.hasBinding()) {
186      line.addString(ed.getBinding().getStrength()!=null ? ed.getBinding().getStrength().toCode() : "");
187      line.addString(ed.getBinding().getDescription());
188      if (ed.getBinding().getValueSet()==null)
189        line.addString("");
190      else
191        line.addString(ed.getBinding().getValueSet());
192    } else {
193      line.addValue("");
194      line.addValue("");
195      line.addValue("");
196    }
197    line.addString(itemList(ed.getCode()));
198    if (ed.hasSlicing()) {
199      line.addString(itemList(ed.getSlicing().getDiscriminator()));
200      line.addString(ed.getSlicing().getDescription());
201      line.addBoolean(ed.getSlicing().getOrdered());
202      line.addString(ed.getSlicing().getRules()!=null ? ed.getSlicing().getRules().toCode() : "");
203    } else {
204      line.addValue("");
205      line.addValue("");
206      line.addValue("");      
207    }
208    if (ed.getBase()!=null) {
209      line.addString(ed.getBase().getPath());
210      line.addValue(ed.getBase().getMin());
211      line.addValue(ed.getBase().getMax());
212    } else {
213      line.addValue("");
214      line.addValue("");
215      line.addValue("");      
216    }
217    line.addString(itemList(ed.getCondition()));
218    line.addString(itemList(ed.getConstraint()));
219    for (StructureDefinitionMappingComponent mapKey : def.getMapping()) {
220      for (ElementDefinitionMappingComponent map : ed.getMapping()) {
221        if (map.getIdentity().equals(mapKey.getIdentity()))
222                line.addString(map.getMap());
223      }
224    }
225  }
226
227
228  private String itemList(List l) {
229    StringBuilder s = new StringBuilder();
230    for (int i =0; i< l.size(); i++) {
231      Object o = l.get(i);
232      String val = "";
233      if (o instanceof StringType) {
234        val = ((StringType)o).getValue();
235      } else if (o instanceof UriType) {
236        val = ((UriType)o).getValue();
237      } else if (o instanceof IdType) {
238        val = ((IdType)o).getValue();
239      } else if (o instanceof Enumeration<?>) {
240        val = o.toString();
241      } else if (o instanceof TypeRefComponent) {
242        TypeRefComponent t = (TypeRefComponent)o;
243          val = t.getWorkingCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}")  + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
244      } else if (o instanceof Coding) {
245        Coding t = (Coding)o;
246        val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")");
247      } else if (o instanceof ElementDefinitionConstraintComponent) {
248        ElementDefinitionConstraintComponent c = (ElementDefinitionConstraintComponent)o;
249        val = c.getKey() + ":" + c.getHuman() + " {" + c.getExpression() + "}";
250      } else if (o instanceof ElementDefinitionSlicingDiscriminatorComponent) {
251        ElementDefinitionSlicingDiscriminatorComponent c = (ElementDefinitionSlicingDiscriminatorComponent)o;
252        val = c.getType().toCode() + ":" + c.getPath() + "}";
253        
254      } else {
255        val = o.toString();
256        val = val.substring(val.indexOf("[")+1);
257        val = val.substring(0, val.indexOf("]"));
258      }
259      s = s.append(val);
260      if (i == 0)
261        s.append("\n");
262    }
263    return s.toString();
264  }
265  
266  private String renderType(DataType value) throws Exception {
267    String s = null;
268    ByteArrayOutputStream bs = new ByteArrayOutputStream();
269    if (asXml) {
270      xml.setOutputStyle(OutputStyle.PRETTY);
271      xml.compose(bs, "", value);
272      bs.close();
273      s = bs.toString();
274      s = s.substring(s.indexOf("\n")+2);
275    } else {
276      json.setOutputStyle(OutputStyle.PRETTY);
277      json.compose(bs, value, "");
278      bs.close();
279      s = bs.toString();
280        }
281    return s;
282  }
283
284  public void dump() throws IOException {
285    for (CSVLine l : lines)
286      ln(l.toString());
287    
288    flush();
289    close();
290  }
291
292}