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