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}