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