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 if (ed.getBinding().getValueSet() instanceof CanonicalType) 162 line.addString(ed.getBinding().getValueSetCanonicalType().getValue()); 163 else 164 line.addString(ed.getBinding().getValueSetUriType().getValue()); 165 } else { 166 line.addValue(""); 167 line.addValue(""); 168 line.addValue(""); 169 } 170 line.addString(itemList(ed.getCode())); 171 if (ed.hasSlicing()) { 172 line.addString(itemList(ed.getSlicing().getDiscriminator())); 173 line.addString(ed.getSlicing().getDescription()); 174 line.addBoolean(ed.getSlicing().getOrdered()); 175 line.addString(ed.getSlicing().getRules()!=null ? ed.getSlicing().getRules().toCode() : ""); 176 } else { 177 line.addValue(""); 178 line.addValue(""); 179 line.addValue(""); 180 } 181 if (ed.getBase()!=null) { 182 line.addString(ed.getBase().getPath()); 183 line.addValue(ed.getBase().getMin()); 184 line.addValue(ed.getBase().getMax()); 185 } else { 186 line.addValue(""); 187 line.addValue(""); 188 line.addValue(""); 189 } 190 line.addString(itemList(ed.getCondition())); 191 line.addString(itemList(ed.getConstraint())); 192 for (StructureDefinitionMappingComponent mapKey : def.getMapping()) { 193 for (ElementDefinitionMappingComponent map : ed.getMapping()) { 194 if (map.getIdentity().equals(mapKey.getIdentity())) 195 line.addString(map.getMap()); 196 } 197 } 198 } 199 200 201 private String itemList(List l) { 202 StringBuilder s = new StringBuilder(); 203 for (int i =0; i< l.size(); i++) { 204 Object o = l.get(i); 205 String val = ""; 206 if (o instanceof StringType) { 207 val = ((StringType)o).getValue(); 208 } else if (o instanceof UriType) { 209 val = ((UriType)o).getValue(); 210 } else if (o instanceof IdType) { 211 val = ((IdType)o).getValue(); 212 } else if (o instanceof Enumeration<?>) { 213 val = o.toString(); 214 } else if (o instanceof TypeRefComponent) { 215 TypeRefComponent t = (TypeRefComponent)o; 216 val = t.getCode() + (t.getProfile() == null ? "" : " {" + t.getProfile() + "}") +(t.getTargetProfile() == null ? "" : " {" + t.getTargetProfile() + "}") + (t.getAggregation() == null || t.getAggregation().isEmpty() ? "" : " (" + itemList(t.getAggregation()) + ")"); 217 } else if (o instanceof Coding) { 218 Coding t = (Coding)o; 219 val = (t.getSystem()==null ? "" : t.getSystem()) + (t.getCode()==null ? "" : "#" + t.getCode()) + (t.getDisplay()==null ? "" : " (" + t.getDisplay() + ")"); 220 } else if (o instanceof ElementDefinitionConstraintComponent) { 221 ElementDefinitionConstraintComponent c = (ElementDefinitionConstraintComponent)o; 222 val = c.getKey() + ":" + c.getHuman() + " {" + c.getExpression() + "}"; 223 } else if (o instanceof ElementDefinitionSlicingDiscriminatorComponent) { 224 ElementDefinitionSlicingDiscriminatorComponent c = (ElementDefinitionSlicingDiscriminatorComponent)o; 225 val = c.getType().toCode() + ":" + c.getPath() + "}"; 226 227 } else { 228 val = o.toString(); 229 val = val.substring(val.indexOf("[")+1); 230 val = val.substring(0, val.indexOf("]")); 231 } 232 s = s.append(val); 233 if (i == 0) 234 s.append("\n"); 235 } 236 return s.toString(); 237 } 238 239 private String renderType(Type value) throws Exception { 240 String s = null; 241 ByteArrayOutputStream bs = new ByteArrayOutputStream(); 242 if (asXml) { 243 xml.setOutputStyle(OutputStyle.PRETTY); 244 xml.compose(bs, "", value); 245 bs.close(); 246 s = bs.toString(); 247 s = s.substring(s.indexOf("\n")+2); 248 } else { 249 json.setOutputStyle(OutputStyle.PRETTY); 250 json.compose(bs, value, ""); 251 bs.close(); 252 s = bs.toString(); 253 } 254 return s; 255 } 256 257 public void dump() throws IOException { 258 for (CSVLine l : lines) 259 ln(l.toString()); 260 261 flush(); 262 close(); 263 } 264 265}