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}