001package org.hl7.fhir.r4.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
032import java.io.ByteArrayOutputStream;
033import java.io.IOException;
034import java.io.OutputStream;
035import java.io.UnsupportedEncodingException;
036import java.util.ArrayList;
037import java.util.Enumeration;
038import java.util.List;
039
040import org.hl7.fhir.r4.formats.IParser.OutputStyle;
041import org.hl7.fhir.r4.formats.JsonParser;
042import org.hl7.fhir.r4.formats.XmlParser;
043import org.hl7.fhir.r4.model.Coding;
044import org.hl7.fhir.r4.model.ElementDefinition;
045import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionConstraintComponent;
046import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionMappingComponent;
047import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent;
048import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
049import org.hl7.fhir.r4.model.IdType;
050import org.hl7.fhir.r4.model.StringType;
051import org.hl7.fhir.r4.model.StructureDefinition;
052import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionMappingComponent;
053import org.hl7.fhir.r4.model.Type;
054import org.hl7.fhir.r4.model.UriType;
055import org.hl7.fhir.utilities.TextStreamWriter;
056
057public class CSVWriter extends TextStreamWriter {
058
059  private StructureDefinition def;
060  private List<StructureDefinitionMappingComponent> mapKeys = new ArrayList<StructureDefinitionMappingComponent>();
061  private List<CSVLine> lines = new ArrayList<CSVLine>();
062  private XmlParser xml = new XmlParser();
063  private JsonParser json = new JsonParser();
064  private boolean asXml;
065
066  private class CSVLine {
067    private String line = "";
068
069    public void addString(String s) {
070      line = line + (line.equals("") ? "" : ",") + "\"" + csvEscape(s) + "\"";
071    }
072
073    public void addString(StringType s) {
074      addString(s == null ? "" : s.getValue());
075    }
076
077    public void addValue(String s) {
078      line = line + (line.equals("") ? "" : ",") + s;
079    }
080
081    public void addValue(int s) {
082      line = line + (line.equals("") ? "" : ",") + s;
083    }
084
085    public void addBoolean(boolean b) {
086      addValue(b ? "Y" : "");
087    }
088
089    protected String csvEscape(String s) {
090      if (s == null)
091        return "";
092      else if (s.contains("\""))
093        return s.substring(0, s.indexOf("\"")) + "\"\"" + csvEscape(s.substring(s.indexOf("\"") + 1));
094      else
095        return s;
096    }
097
098    public String toString() {
099      return line;
100    }
101  }
102
103  public CSVWriter(OutputStream out, StructureDefinition def, boolean asXml) throws UnsupportedEncodingException {
104    super(out);
105    this.asXml = asXml;
106    this.def = def;
107    CSVLine header = new CSVLine();
108    lines.add(header);
109    header.addString("Path"); // A
110    header.addString("Slice Name"); // B
111    header.addString("Alias(s)"); // C
112    header.addString("Label"); // D
113    header.addString("Min"); // E
114    header.addString("Max"); // F
115    header.addString("Must Support?"); // G
116    header.addString("Is Modifier?"); // H
117    header.addString("Is Summary?"); // I
118    header.addString("Type(s)"); // J
119    header.addString("Short"); // K
120    header.addString("Definition"); // L
121    header.addString("Comments"); // M
122    header.addString("Requirements"); // N
123    header.addString("Default Value"); // O
124    header.addString("Meaning When Missing"); // P
125    header.addString("Fixed Value"); // Q
126    header.addString("Pattern"); // R
127    header.addString("Example"); // S
128    header.addString("Minimum Value"); // T
129    header.addString("Maximum Value"); // U
130    header.addString("Maximum Length"); // V
131    header.addString("Binding Strength"); // W
132    header.addString("Binding Description"); // X
133    header.addString("Binding Value Set"); // Y
134    header.addString("Code"); // Z
135    header.addString("Slicing Discriminator");// AA
136    header.addString("Slicing Description"); // AB
137    header.addString("Slicing Ordered"); // AC
138    header.addString("Slicing Rules"); // AD
139    header.addString("Base Path"); // AE
140    header.addString("Base Min"); // AF
141    header.addString("Base Max"); // AG
142    header.addString("Condition(s)"); // AH
143    header.addString("Constraint(s)"); // AI
144    for (StructureDefinitionMappingComponent map : def.getMapping()) {
145      header.addString("Mapping: " + map.getName());
146    }
147  }
148
149  /*
150   * private void findMapKeys(StructureDefinition def,
151   * List<StructureDefinitionMappingComponent> maps, IWorkerContext context) {
152   * maps.addAll(def.getMapping()); if (def.getBaseDefinition()!=null) {
153   * StructureDefinition base = context.fetchResource(StructureDefinition.class,
154   * def.getBaseDefinition()); findMapKeys(base, maps, context); } }
155   */
156
157  public void processElement(ElementDefinition ed) throws Exception {
158    CSVLine line = new CSVLine();
159    lines.add(line);
160    line.addString(ed.getPath());
161    line.addString(ed.getSliceName());
162    line.addString(itemList(ed.getAlias()));
163    line.addString(ed.getLabel());
164    line.addValue(ed.getMin());
165    line.addValue(ed.getMax());
166    line.addString(ed.getMustSupport() ? "Y" : "");
167    line.addString(ed.getIsModifier() ? "Y" : "");
168    line.addString(ed.getIsSummary() ? "Y" : "");
169    line.addString(itemList(ed.getType()));
170    line.addString(ed.getShort());
171    line.addString(ed.getDefinition());
172    line.addString(ed.getComment());
173    line.addString(ed.getRequirements());
174    line.addString(ed.getDefaultValue() != null ? renderType(ed.getDefaultValue()) : "");
175    line.addString(ed.getMeaningWhenMissing());
176    line.addString(ed.hasFixed() ? renderType(ed.getFixed()) : "");
177    line.addString(ed.hasPattern() ? renderType(ed.getPattern()) : "");
178    line.addString(ed.hasExample() ? renderType(ed.getExample().get(0).getValue()) : ""); // todo...?
179    line.addString(ed.hasMinValue() ? renderType(ed.getMinValue()) : "");
180    line.addString(ed.hasMaxValue() ? renderType(ed.getMaxValue()) : "");
181    line.addValue((ed.hasMaxLength() ? Integer.toString(ed.getMaxLength()) : ""));
182    if (ed.hasBinding()) {
183      line.addString(ed.getBinding().getStrength() != null ? ed.getBinding().getStrength().toCode() : "");
184      line.addString(ed.getBinding().getDescription());
185      if (ed.getBinding().getValueSet() == null)
186        line.addString("");
187      else
188        line.addString(ed.getBinding().getValueSet());
189    } else {
190      line.addValue("");
191      line.addValue("");
192      line.addValue("");
193    }
194    line.addString(itemList(ed.getCode()));
195    if (ed.hasSlicing()) {
196      line.addString(itemList(ed.getSlicing().getDiscriminator()));
197      line.addString(ed.getSlicing().getDescription());
198      line.addBoolean(ed.getSlicing().getOrdered());
199      line.addString(ed.getSlicing().getRules() != null ? ed.getSlicing().getRules().toCode() : "");
200    } else {
201      line.addValue("");
202      line.addValue("");
203      line.addValue("");
204    }
205    if (ed.getBase() != null) {
206      line.addString(ed.getBase().getPath());
207      line.addValue(ed.getBase().getMin());
208      line.addValue(ed.getBase().getMax());
209    } else {
210      line.addValue("");
211      line.addValue("");
212      line.addValue("");
213    }
214    line.addString(itemList(ed.getCondition()));
215    line.addString(itemList(ed.getConstraint()));
216    for (StructureDefinitionMappingComponent mapKey : def.getMapping()) {
217      for (ElementDefinitionMappingComponent map : ed.getMapping()) {
218        if (map.getIdentity().equals(mapKey.getIdentity()))
219          line.addString(map.getMap());
220      }
221    }
222  }
223
224  private String itemList(List l) {
225    StringBuilder s = new StringBuilder();
226    for (int i = 0; i < l.size(); i++) {
227      Object o = l.get(i);
228      String val = "";
229      if (o instanceof StringType) {
230        val = ((StringType) o).getValue();
231      } else if (o instanceof UriType) {
232        val = ((UriType) o).getValue();
233      } else if (o instanceof IdType) {
234        val = ((IdType) o).getValue();
235      } else if (o instanceof Enumeration<?>) {
236        val = o.toString();
237      } else if (o instanceof TypeRefComponent) {
238        TypeRefComponent t = (TypeRefComponent) o;
239        val = t.getWorkingCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}")
240            + (t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}")
241            + (t.getAggregation() == null || t.getAggregation().isEmpty() ? ""
242                : " (" + itemList(t.getAggregation()) + ")");
243      } else if (o instanceof Coding) {
244        Coding t = (Coding) o;
245        val = (t.getSystem() == null ? "" : t.getSystem()) + (t.getCode() == null ? "" : "#" + t.getCode())
246            + (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(Type 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}