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