
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.HashMap; 039import java.util.List; 040import java.util.Map; 041 042import org.apache.poi.ss.usermodel.BorderStyle; 043import org.apache.poi.ss.usermodel.Cell; 044import org.apache.poi.ss.usermodel.CellStyle; 045import org.apache.poi.ss.usermodel.ConditionalFormattingRule; 046import org.apache.poi.ss.usermodel.FillPatternType; 047import org.apache.poi.ss.usermodel.Font; 048import org.apache.poi.ss.usermodel.FontFormatting; 049import org.apache.poi.ss.usermodel.IndexedColors; 050import org.apache.poi.ss.usermodel.PatternFormatting; 051import org.apache.poi.ss.usermodel.Row; 052import org.apache.poi.ss.usermodel.Sheet; 053import org.apache.poi.ss.usermodel.SheetConditionalFormatting; 054import org.apache.poi.ss.usermodel.VerticalAlignment; 055import org.apache.poi.ss.usermodel.Workbook; 056import org.apache.poi.ss.util.CellAddress; 057import org.apache.poi.ss.util.CellRangeAddress; 058import org.apache.poi.xssf.usermodel.XSSFRow; 059import org.apache.poi.xssf.usermodel.XSSFSheet; 060import org.apache.poi.xssf.usermodel.XSSFWorkbook; 061import org.hl7.fhir.r4.formats.IParser.OutputStyle; 062import org.hl7.fhir.r4.formats.JsonParser; 063import org.hl7.fhir.r4.formats.XmlParser; 064import org.hl7.fhir.r4.model.CanonicalType; 065import org.hl7.fhir.r4.model.Coding; 066import org.hl7.fhir.r4.model.ElementDefinition; 067import org.hl7.fhir.r4.model.ElementDefinition.AggregationMode; 068import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionConstraintComponent; 069import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionMappingComponent; 070import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; 071import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent; 072import org.hl7.fhir.r4.model.IdType; 073import org.hl7.fhir.r4.model.StringType; 074import org.hl7.fhir.r4.model.StructureDefinition; 075import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionMappingComponent; 076import org.hl7.fhir.r4.model.Type; 077import org.hl7.fhir.r4.model.UriType; 078import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 079import org.hl7.fhir.utilities.TextStreamWriter; 080import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTAutoFilter; 081import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCustomFilter; 082import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCustomFilters; 083import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFilterColumn; 084import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFilters; 085import org.openxmlformats.schemas.spreadsheetml.x2006.main.STFilterOperator; 086 087@Deprecated 088public class XLSXWriter extends TextStreamWriter { 089 090 private StructureDefinition def; 091 private List<StructureDefinitionMappingComponent> mapKeys = new ArrayList<StructureDefinitionMappingComponent>(); 092 private Map<String, CellStyle> styles; 093 private OutputStream outStream; 094 private XSSFWorkbook wb = new XSSFWorkbook(); 095 private Sheet sheet; 096 private XmlParser xml = new XmlParser(); 097 private JsonParser json = new JsonParser(); 098 private boolean asXml; 099 private boolean hideMustSupportFalse; 100 101 private static String[] titles = { "Path", "Slice Name", "Alias(s)", "Label", "Min", "Max", "Must Support?", 102 "Is Modifier?", "Is Summary?", "Type(s)", "Short", "Definition", "Comments", "Requirements", "Default Value", 103 "Meaning When Missing", "Fixed Value", "Pattern", "Example", "Minimum Value", "Maximum Value", "Maximum Length", 104 "Binding Strength", "Binding Description", "Binding Value Set", "Code", "Slicing Discriminator", 105 "Slicing Description", "Slicing Ordered", "Slicing Rules", "Base Path", "Base Min", "Base Max", "Condition(s)", 106 "Constraint(s)" }; 107 108 public XLSXWriter(OutputStream out, StructureDefinition def, boolean asXml, boolean hideMustSupportFalse) 109 throws UnsupportedEncodingException { 110 super(out); 111 outStream = out; 112 this.asXml = asXml; 113 this.def = def; 114 this.hideMustSupportFalse = hideMustSupportFalse; 115 sheet = wb.createSheet("Elements"); 116 styles = createStyles(wb); 117 Row headerRow = sheet.createRow(0); 118 for (int i = 0; i < titles.length; i++) { 119 addCell(headerRow, i, titles[i], styles.get("header")); 120 } 121 int i = titles.length - 1; 122 for (StructureDefinitionMappingComponent map : def.getMapping()) { 123 i++; 124 addCell(headerRow, i, "Mapping: " + map.getName(), styles.get("header")); 125 } 126 } 127 128 private void addCell(Row row, int pos, String content) { 129 addCell(row, pos, content, styles.get("body")); 130 } 131 132 public void addCell(Row row, int pos, boolean b) { 133 addCell(row, pos, b ? "Y" : ""); 134 } 135 136 public void addCell(Row row, int pos, int content) { 137 addCell(row, pos, Integer.toString(content)); 138 } 139 140 private void addCell(Row row, int pos, String content, CellStyle style) { 141 Cell cell = row.createCell(pos); 142 cell.setCellValue(content); 143 cell.setCellStyle(style); 144 } 145 146 /** 147 * create a library of cell styles 148 */ 149 private static Map<String, CellStyle> createStyles(Workbook wb) { 150 Map<String, CellStyle> styles = new HashMap<>(); 151 152 CellStyle style; 153 Font headerFont = wb.createFont(); 154 headerFont.setBold(true); 155 style = createBorderedStyle(wb); 156 style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex()); 157 style.setFillPattern(FillPatternType.SOLID_FOREGROUND); 158 style.setVerticalAlignment(VerticalAlignment.TOP); 159 style.setWrapText(true); 160 style.setFont(headerFont); 161 styles.put("header", style); 162 163 style = createBorderedStyle(wb); 164 style.setVerticalAlignment(VerticalAlignment.TOP); 165 style.setWrapText(true); 166 styles.put("body", style); 167 168 return styles; 169 } 170 171 private static CellStyle createBorderedStyle(Workbook wb) { 172 BorderStyle thin = BorderStyle.THIN; 173 short black = IndexedColors.GREY_50_PERCENT.getIndex(); 174 175 CellStyle style = wb.createCellStyle(); 176 style.setBorderRight(thin); 177 style.setRightBorderColor(black); 178 style.setBorderBottom(thin); 179 style.setBottomBorderColor(black); 180 style.setBorderLeft(thin); 181 style.setLeftBorderColor(black); 182 style.setBorderTop(thin); 183 style.setTopBorderColor(black); 184 return style; 185 } 186 /* 187 * private void findMapKeys(StructureDefinition def, 188 * List<StructureDefinitionMappingComponent> maps, IWorkerContext context) { 189 * maps.addAll(def.getMapping()); if (def.getBaseDefinition()!=null) { 190 * StructureDefinition base = context.fetchResource(StructureDefinition.class, 191 * def.getBaseDefinition()); findMapKeys(base, maps, context); } } 192 */ 193 194 public void processElement(ElementDefinition ed) throws Exception { 195 Row row = sheet.createRow(sheet.getLastRowNum() + 1); 196 int i = 0; 197 addCell(row, i++, ed.getPath(), styles.get("body")); 198 addCell(row, i++, ed.getSliceName()); 199 addCell(row, i++, itemList(ed.getAlias())); 200 addCell(row, i++, ed.getLabel()); 201 addCell(row, i++, ed.getMin()); 202 addCell(row, i++, ed.getMax()); 203 addCell(row, i++, ed.getMustSupport() ? "Y" : ""); 204 addCell(row, i++, ed.getIsModifier() ? "Y" : ""); 205 addCell(row, i++, ed.getIsSummary() ? "Y" : ""); 206 addCell(row, i++, itemList(ed.getType())); 207 addCell(row, i++, ed.getShort()); 208 addCell(row, i++, ed.getDefinition()); 209 addCell(row, i++, ed.getComment()); 210 addCell(row, i++, ed.getRequirements()); 211 addCell(row, i++, ed.getDefaultValue() != null ? renderType(ed.getDefaultValue()) : ""); 212 addCell(row, i++, ed.getMeaningWhenMissing()); 213 addCell(row, i++, ed.hasFixed() ? renderType(ed.getFixed()) : ""); 214 addCell(row, i++, ed.hasPattern() ? renderType(ed.getPattern()) : ""); 215 addCell(row, i++, ed.hasExample() ? renderType(ed.getExample().get(0).getValue()) : ""); // todo...? 216 addCell(row, i++, ed.hasMinValue() ? renderType(ed.getMinValue()) : ""); 217 addCell(row, i++, ed.hasMaxValue() ? renderType(ed.getMaxValue()) : ""); 218 addCell(row, i++, (ed.hasMaxLength() ? Integer.toString(ed.getMaxLength()) : "")); 219 if (ed.hasBinding()) { 220 addCell(row, i++, ed.getBinding().getStrength() != null ? ed.getBinding().getStrength().toCode() : ""); 221 addCell(row, i++, ed.getBinding().getDescription()); 222 if (ed.getBinding().getValueSet() == null) 223 addCell(row, i++, ""); 224 else 225 addCell(row, i++, ed.getBinding().getValueSet()); 226 } else { 227 addCell(row, i++, ""); 228 addCell(row, i++, ""); 229 addCell(row, i++, ""); 230 } 231 addCell(row, i++, itemList(ed.getCode())); 232 if (ed.hasSlicing()) { 233 addCell(row, i++, itemList(ed.getSlicing().getDiscriminator())); 234 addCell(row, i++, ed.getSlicing().getDescription()); 235 addCell(row, i++, ed.getSlicing().getOrdered()); 236 addCell(row, i++, ed.getSlicing().getRules() != null ? ed.getSlicing().getRules().toCode() : ""); 237 } else { 238 addCell(row, i++, ""); 239 addCell(row, i++, ""); 240 addCell(row, i++, ""); 241 addCell(row, i++, ""); 242 } 243 if (ed.getBase() != null) { 244 addCell(row, i++, ed.getBase().getPath()); 245 addCell(row, i++, ed.getBase().getMin()); 246 addCell(row, i++, ed.getBase().getMax()); 247 } else { 248 addCell(row, i++, ""); 249 addCell(row, i++, ""); 250 addCell(row, i++, ""); 251 } 252 addCell(row, i++, itemList(ed.getCondition())); 253 addCell(row, i++, itemList(ed.getConstraint())); 254 for (StructureDefinitionMappingComponent mapKey : def.getMapping()) { 255 String mapString = ""; 256 for (ElementDefinitionMappingComponent map : ed.getMapping()) { 257 if (map.getIdentity().equals(mapKey.getIdentity())) 258 mapString = map.getMap(); 259 } 260 addCell(row, i++, mapString); 261 } 262 } 263 264 private String itemList(List l) { 265 StringBuilder s = new StringBuilder(); 266 for (int i = 0; i < l.size(); i++) { 267 Object o = l.get(i); 268 String val = ""; 269 if (o instanceof StringType) { 270 val = ((StringType) o).getValue(); 271 } else if (o instanceof UriType) { 272 val = ((UriType) o).getValue(); 273 } else if (o instanceof IdType) { 274 val = ((IdType) o).getValue(); 275 } else if (o instanceof Enumeration<?>) { 276 val = o.toString(); 277 } else if (o instanceof TypeRefComponent) { 278 TypeRefComponent t = (TypeRefComponent) o; 279 val = t.getWorkingCode(); 280 if (val == null) 281 val = ""; 282 if (val.startsWith("http://hl7.org/fhir/StructureDefinition/")) 283 val = val.substring(40); 284 if (t.hasTargetProfile()) 285 val = val + "(" + canonicalList(t.getTargetProfile()) + ")"; 286 if (t.hasProfile()) 287 val = val + " {" + canonicalList(t.getProfile()) + "}"; 288 if (t.hasAggregation()) 289 val = val + " <<" + aggList(t.getAggregation()) + ">>"; 290 } else if (o instanceof Coding) { 291 Coding t = (Coding) o; 292 val = (t.getSystem() == null ? "" : t.getSystem()) + (t.getCode() == null ? "" : "#" + t.getCode()) 293 + (t.getDisplay() == null ? "" : " (" + t.getDisplay() + ")"); 294 } else if (o instanceof ElementDefinitionConstraintComponent) { 295 ElementDefinitionConstraintComponent c = (ElementDefinitionConstraintComponent) o; 296 val = c.getKey() + ":" + c.getHuman() + " {" + c.getExpression() + "}"; 297 } else if (o instanceof ElementDefinitionSlicingDiscriminatorComponent) { 298 ElementDefinitionSlicingDiscriminatorComponent c = (ElementDefinitionSlicingDiscriminatorComponent) o; 299 val = c.getType().toCode() + ":" + c.getPath() + "}"; 300 301 } else { 302 val = o.toString(); 303 val = val.substring(val.indexOf("[") + 1); 304 val = val.substring(0, val.indexOf("]")); 305 } 306 s = s.append(val); 307 if (i == 0) 308 s.append("\n"); 309 } 310 return s.toString(); 311 } 312 313 private String aggList(List<org.hl7.fhir.r4.model.Enumeration<AggregationMode>> list) { 314 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 315 for (org.hl7.fhir.r4.model.Enumeration<AggregationMode> c : list) 316 b.append(c.getValue().toCode()); 317 return b.toString(); 318 } 319 320 private String canonicalList(List<CanonicalType> list) { 321 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("|"); 322 for (CanonicalType c : list) { 323 String v = c.getValue(); 324 if (v.startsWith("http://hl7.org/fhir/StructureDefinition/")) 325 v = v.substring(40); 326 b.append(v); 327 } 328 return b.toString(); 329 } 330 331 private String renderType(Type value) throws Exception { 332 if (value == null) 333 return ""; 334 if (value.isPrimitive()) 335 return value.primitiveValue(); 336 337 String s = null; 338 ByteArrayOutputStream bs = new ByteArrayOutputStream(); 339 if (asXml) { 340 xml.setOutputStyle(OutputStyle.PRETTY); 341 xml.compose(bs, "", value); 342 bs.close(); 343 s = bs.toString(); 344 s = s.substring(s.indexOf("\n") + 2); 345 } else { 346 json.setOutputStyle(OutputStyle.PRETTY); 347 json.compose(bs, value, ""); 348 bs.close(); 349 s = bs.toString(); 350 } 351 return s; 352 } 353 354 private int columnPixels(double columns) { 355 double WIDTH_FACTOR = 256; 356 double PADDING = 180; 357 return (int) Math.floor(columns * WIDTH_FACTOR + PADDING); 358 } 359 360 public void dump() throws IOException { 361 for (int i = 0; i < 34; i++) { 362 sheet.autoSizeColumn(i); 363 } 364 365 sheet.setColumnHidden(2, true); 366 sheet.setColumnHidden(3, true); 367 sheet.setColumnHidden(30, true); 368 sheet.setColumnHidden(31, true); 369 sheet.setColumnHidden(32, true); 370 371 sheet.setColumnWidth(9, columnPixels(20)); 372 sheet.setColumnWidth(11, columnPixels(100)); 373 sheet.setColumnWidth(12, columnPixels(100)); 374 sheet.setColumnWidth(13, columnPixels(100)); 375 sheet.setColumnWidth(15, columnPixels(20)); 376 sheet.setColumnWidth(16, columnPixels(20)); 377 sheet.setColumnWidth(17, columnPixels(20)); 378 sheet.setColumnWidth(18, columnPixels(20)); 379 sheet.setColumnWidth(34, columnPixels(100)); 380 381 int i = titles.length - 1; 382 for (StructureDefinitionMappingComponent map : def.getMapping()) { 383 i++; 384 sheet.setColumnWidth(i, columnPixels(50)); 385 sheet.autoSizeColumn(i); 386// sheet.setColumnHidden(i, true); 387 } 388 sheet.createFreezePane(2, 1); 389 390 if (hideMustSupportFalse) { 391 SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting(); 392 String address = "A2:AI" + Math.max(Integer.valueOf(sheet.getLastRowNum()), 2); 393 CellRangeAddress[] regions = { CellRangeAddress.valueOf(address) }; 394 395 ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("$G2<>\"Y\""); 396 PatternFormatting fill1 = rule1.createPatternFormatting(); 397 fill1.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index); 398 fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND); 399 400 ConditionalFormattingRule rule2 = sheetCF.createConditionalFormattingRule("$Q2<>\"\""); 401 FontFormatting font = rule2.createFontFormatting(); 402 font.setFontColorIndex(IndexedColors.GREY_25_PERCENT.index); 403 font.setFontStyle(true, false); 404 405 sheetCF.addConditionalFormatting(regions, rule1, rule2); 406 407 sheet.setAutoFilter( 408 new CellRangeAddress(0, sheet.getLastRowNum(), 0, titles.length + def.getMapping().size() - 1)); 409 410 XSSFSheet xSheet = (XSSFSheet) sheet; 411 412 CTAutoFilter sheetFilter = xSheet.getCTWorksheet().getAutoFilter(); 413 CTFilterColumn filterColumn1 = sheetFilter.addNewFilterColumn(); 414 filterColumn1.setColId(6); 415 CTCustomFilters filters = filterColumn1.addNewCustomFilters(); 416 CTCustomFilter filter1 = filters.addNewCustomFilter(); 417 filter1.setOperator(STFilterOperator.NOT_EQUAL); 418 filter1.setVal(" "); 419 420 CTFilterColumn filterColumn2 = sheetFilter.addNewFilterColumn(); 421 filterColumn2.setColId(26); 422 CTFilters filters2 = filterColumn2.addNewFilters(); 423 filters2.setBlank(true); 424 425 // We have to apply the filter ourselves by hiding the rows: 426 for (Row row : sheet) { 427 if (row.getRowNum() > 0 428 && (!row.getCell(6).getStringCellValue().equals("Y") || !row.getCell(26).getStringCellValue().isEmpty())) { 429 ((XSSFRow) row).getCTRow().setHidden(true); 430 } 431 } 432 } 433 sheet.setActiveCell(new CellAddress(sheet.getRow(1).getCell(0))); 434 435 wb.write(outStream); 436 437 flush(); 438 close(); 439 } 440 441}