001package org.hl7.fhir.dstu3.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.dstu3.formats.IParser.OutputStyle;
043import org.hl7.fhir.dstu3.formats.JsonParser;
044import org.hl7.fhir.dstu3.formats.XmlParser;
045import org.hl7.fhir.dstu3.model.Coding;
046import org.hl7.fhir.dstu3.model.ElementDefinition;
047import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionConstraintComponent;
048import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionMappingComponent;
049import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent;
050import org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent;
051import org.hl7.fhir.dstu3.model.IdType;
052import org.hl7.fhir.dstu3.model.Reference;
053import org.hl7.fhir.dstu3.model.StringType;
054import org.hl7.fhir.dstu3.model.StructureDefinition;
055import org.hl7.fhir.dstu3.model.StructureDefinition.StructureDefinitionMappingComponent;
056import org.hl7.fhir.dstu3.model.Type;
057import org.hl7.fhir.dstu3.model.UriType;
058import org.hl7.fhir.utilities.TextStreamWriter;
059
060
061public class CSVWriter  extends TextStreamWriter  {
062
063  private StructureDefinition def;
064  private List<StructureDefinitionMappingComponent> mapKeys = new ArrayList<StructureDefinitionMappingComponent>();
065  private List<CSVLine> lines = new ArrayList<CSVLine>();
066  private XmlParser xml = new XmlParser();
067  private JsonParser json = new JsonParser();
068  private boolean asXml;
069
070  private class CSVLine {
071    private String line = "";
072    
073    public void addString(String s) {
074      line = line + (line.equals("") ? "":",") + "\"" + csvEscape(s) + "\"";
075    }
076    
077    public void addString(StringType s) {
078      addString(s==null? "" : s.getValue());
079    }
080
081    public void addValue(String s) {
082      line = line + (line.equals("") ? "":",") + s;
083    }
084    
085    public void addValue(int s) {
086      line = line + (line.equals("") ? "":",") + s;
087    }
088
089    public void addBoolean(boolean b) {
090      addValue(b ? "Y" : "");
091    }
092
093    protected String csvEscape(String s) {
094      if (s==null)
095        return "";
096      else if (s.contains("\""))
097          return s.substring(0,s.indexOf("\"")) + "\"\"" + csvEscape(s.substring(s.indexOf("\"")+1));
098      else
099        return s;
100    }
101    
102    public String toString() {
103      return line;
104    }
105  }
106
107  public CSVWriter(OutputStream out, StructureDefinition def, boolean asXml) throws UnsupportedEncodingException {
108    super(out);
109    this.asXml = asXml;
110    this.def = def;
111    CSVLine header = new CSVLine();
112    lines.add(header);
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/*  private void findMapKeys(StructureDefinition def, List<StructureDefinitionMappingComponent> maps, IWorkerContext context) {
154        maps.addAll(def.getMapping());
155        if (def.getBaseDefinition()!=null) {
156          StructureDefinition base = context.fetchResource(StructureDefinition.class, def.getBaseDefinition());
157          findMapKeys(base, maps, context);
158        }
159  }*/
160
161  public void processElement(ElementDefinition ed) throws Exception {
162    CSVLine line = new CSVLine();
163    lines.add(line);
164    line.addString(ed.getPath());
165    line.addString(ed.getSliceName());
166    line.addString(itemList(ed.getAlias()));
167    line.addString(ed.getLabel());
168    line.addValue(ed.getMin());
169    line.addValue(ed.getMax());
170    line.addString(ed.getMustSupport() ? "Y" : "");
171    line.addString(ed.getIsModifier() ? "Y" : "");
172    line.addString(ed.getIsSummary() ? "Y" : "");
173    line.addString(itemList(ed.getType()));
174    line.addString(ed.getShort());
175    line.addString(ed.getDefinition());
176    line.addString(ed.getComment());
177    line.addString(ed.getRequirements());
178    line.addString(ed.getDefaultValue()!=null ? renderType(ed.getDefaultValue()) : "");
179    line.addString(ed.getMeaningWhenMissing());
180    line.addString(ed.hasFixed() ? renderType(ed.getFixed()) : "");
181    line.addString(ed.hasPattern() ? renderType(ed.getPattern()) : "");
182    line.addString(ed.hasExample() ? renderType(ed.getExample().get(0).getValue()) : ""); // todo...?
183    line.addString(ed.hasMinValue() ? renderType(ed.getMinValue()) : "");
184    line.addString(ed.hasMaxValue() ? renderType(ed.getMaxValue()) : "");
185    line.addValue((ed.hasMaxLength() ? Integer.toString(ed.getMaxLength()) : ""));
186    if (ed.hasBinding()) {
187      line.addString(ed.getBinding().getStrength()!=null ? ed.getBinding().getStrength().toCode() : "");
188      line.addString(ed.getBinding().getDescription());
189      if (ed.getBinding().getValueSet()==null)
190        line.addString("");
191      else if (ed.getBinding().getValueSet() instanceof Reference)
192      line.addString(ed.getBinding().getValueSetReference().getReference());
193      else
194      line.addString(ed.getBinding().getValueSetUriType().getValue());
195    } else {
196      line.addValue("");
197      line.addValue("");
198      line.addValue("");
199    }
200    line.addString(itemList(ed.getCode()));
201    if (ed.hasSlicing()) {
202      line.addString(itemList(ed.getSlicing().getDiscriminator()));
203      line.addString(ed.getSlicing().getDescription());
204      line.addBoolean(ed.getSlicing().getOrdered());
205      line.addString(ed.getSlicing().getRules()!=null ? ed.getSlicing().getRules().toCode() : "");
206    } else {
207      line.addValue("");
208      line.addValue("");
209      line.addValue("");      
210    }
211    if (ed.getBase()!=null) {
212      line.addString(ed.getBase().getPath());
213      line.addValue(ed.getBase().getMin());
214      line.addValue(ed.getBase().getMax());
215    } else {
216      line.addValue("");
217      line.addValue("");
218      line.addValue("");      
219    }
220    line.addString(itemList(ed.getCondition()));
221    line.addString(itemList(ed.getConstraint()));
222    for (StructureDefinitionMappingComponent mapKey : def.getMapping()) {
223      for (ElementDefinitionMappingComponent map : ed.getMapping()) {
224        if (map.getIdentity().equals(mapKey.getIdentity()))
225                line.addString(map.getMap());
226      }
227    }
228  }
229
230
231  private String itemList(List l) {
232    StringBuilder s = new StringBuilder();
233    for (int i =0; i< l.size(); i++) {
234      Object o = l.get(i);
235      String val = "";
236      if (o instanceof StringType) {
237        val = ((StringType)o).getValue();
238      } else if (o instanceof UriType) {
239        val = ((UriType)o).getValue();
240      } else if (o instanceof IdType) {
241        val = ((IdType)o).getValue();
242      } else if (o instanceof Enumeration<?>) {
243        val = o.toString();
244      } else if (o instanceof TypeRefComponent) {
245        TypeRefComponent t = (TypeRefComponent)o;
246          val = t.getCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}")  + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")");
247      } else if (o instanceof Coding) {
248        Coding t = (Coding)o;
249        val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")");
250      } else if (o instanceof ElementDefinitionConstraintComponent) {
251        ElementDefinitionConstraintComponent c = (ElementDefinitionConstraintComponent)o;
252        val = c.getKey() + ":" + c.getHuman() + " {" + c.getExpression() + "}";
253      } else if (o instanceof ElementDefinitionSlicingDiscriminatorComponent) {
254        ElementDefinitionSlicingDiscriminatorComponent c = (ElementDefinitionSlicingDiscriminatorComponent)o;
255        val = c.getType().toCode() + ":" + c.getPath() + "}";
256        
257      } else {
258        val = o.toString();
259        val = val.substring(val.indexOf("[")+1);
260        val = val.substring(0, val.indexOf("]"));
261      }
262      s = s.append(val);
263      if (i == 0)
264        s.append("\n");
265    }
266    return s.toString();
267  }
268  
269  private String renderType(Type value) throws Exception {
270    String s = null;
271    ByteArrayOutputStream bs = new ByteArrayOutputStream();
272    if (asXml) {
273      xml.setOutputStyle(OutputStyle.PRETTY);
274      xml.compose(bs, "", value);
275      bs.close();
276      s = bs.toString();
277      s = s.substring(s.indexOf("\n")+2);
278    } else {
279      json.setOutputStyle(OutputStyle.PRETTY);
280      json.compose(bs, value, "");
281      bs.close();
282      s = bs.toString();
283        }
284    return s;
285  }
286
287  public void dump() throws IOException {
288    for (CSVLine l : lines)
289      ln(l.toString());
290    
291    flush();
292    close();
293  }
294
295}