001package org.hl7.fhir.r5.renderers.spreadsheets;
002
003import java.io.IOException;
004import java.io.OutputStream;
005import java.util.ArrayList;
006import java.util.HashMap;
007import java.util.List;
008import java.util.Map;
009
010import org.apache.poi.ss.usermodel.BorderStyle;
011import org.apache.poi.ss.usermodel.Cell;
012import org.apache.poi.ss.usermodel.CellStyle;
013import org.apache.poi.ss.usermodel.FillPatternType;
014import org.apache.poi.ss.usermodel.Font;
015import org.apache.poi.ss.usermodel.IndexedColors;
016import org.apache.poi.ss.usermodel.Row;
017import org.apache.poi.ss.usermodel.Sheet;
018import org.apache.poi.ss.usermodel.VerticalAlignment;
019import org.apache.poi.ss.usermodel.Workbook;
020import org.apache.poi.xssf.usermodel.XSSFWorkbook;
021import org.hl7.fhir.exceptions.FHIRException;
022import org.hl7.fhir.r5.context.IWorkerContext;
023import org.hl7.fhir.r5.renderers.DataRenderer;
024import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
025
026import com.microsoft.schemas.office.visio.x2012.main.ShapeSheetType;
027
028/*
029  Copyright (c) 2011+, HL7, Inc.
030  All rights reserved.
031
032  Redistribution and use in source and binary forms, with or without modification, 
033  are permitted provided that the following conditions are met:
034
035 * Redistributions of source code must retain the above copyright notice, this 
036     list of conditions and the following disclaimer.
037 * Redistributions in binary form must reproduce the above copyright notice, 
038     this list of conditions and the following disclaimer in the documentation 
039     and/or other materials provided with the distribution.
040 * Neither the name of HL7 nor the names of its contributors may be used to 
041     endorse or promote products derived from this software without specific 
042     prior written permission.
043
044  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
045  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
046  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
047  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
048  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
049  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
050  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
051  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
052  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
053  POSSIBILITY OF SUCH DAMAGE.
054
055 */
056
057
058
059@MarkedToMoveToAdjunctPackage
060public class SpreadsheetGenerator {
061    
062  private static final int MAX_SENSITIVE_SHEET_NAME_LEN = 31;
063
064  protected IWorkerContext context;
065
066  protected XSSFWorkbook wb = new XSSFWorkbook();
067  protected Map<String, CellStyle> styles;
068
069  protected DataRenderer dr;
070  private Map<String, Sheet> sheetNames = new HashMap<>();
071  
072  public SpreadsheetGenerator(IWorkerContext context) {
073    super();
074    this.context = context;
075    styles = createStyles(wb);
076    dr = new DataRenderer(context);
077  }
078
079  public void finish(OutputStream outStream) throws IOException {
080    wb.write(outStream);
081    outStream.flush();
082    outStream.close();
083  }
084  
085  protected boolean hasSheet(String name) {
086    if (name.length() > MAX_SENSITIVE_SHEET_NAME_LEN - 2) {
087      name = name.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN - 2);
088    }
089    String s = fixSheetNameChars(name);
090    return sheetNames.containsKey(s);
091  }
092
093  protected Sheet getSheet(String name) {
094    if (name.length() > MAX_SENSITIVE_SHEET_NAME_LEN - 2) {
095      name = name.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN - 2);
096    }
097    String s = fixSheetNameChars(name);
098    return sheetNames.get(s);
099  }
100
101  protected Sheet makeSheet(String name) {
102    if (name.length() > MAX_SENSITIVE_SHEET_NAME_LEN - 2) {
103      name = name.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN - 2);
104    }
105    name = fixSheetNameChars(name);
106    String s = name;
107    if (sheetNames.containsKey(s)) {
108      int i = 1;
109      do {
110        i++;
111        s = name+" "+Integer.toString(i);
112      } while (sheetNames.containsKey(s));
113    }
114    Sheet res = wb.createSheet(s); 
115    sheetNames.put(s, res);
116    return res;
117  }
118
119  private String fixSheetNameChars(String name) {
120    StringBuilder b = new StringBuilder();
121    for (char ch : name.toCharArray()) {
122      switch (ch) {
123        case '/':
124        case '\\':
125        case '?':
126        case '*':
127        case ']':
128        case '[':
129        case ':':
130          b.append('_');
131          break;
132        default:
133          b.append(ch);
134      }
135    }
136    return b.toString();
137  }
138
139  private static Map<String, CellStyle> createStyles(Workbook wb){
140    Map<String, CellStyle> styles = new HashMap<>();
141
142    CellStyle style;
143    Font headerFont = wb.createFont();
144    headerFont.setBold(true);
145    style = createBorderedStyle(wb);
146    style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
147    style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
148    style.setVerticalAlignment(VerticalAlignment.TOP);
149    style.setWrapText(true);
150    style.setFont(headerFont);
151    styles.put("header", style);
152
153    style = createBorderedStyle(wb);
154    style.setVerticalAlignment(VerticalAlignment.TOP);
155    style.setWrapText(true);    
156    styles.put("body", style);
157
158    return styles;
159  }
160
161  private static CellStyle createBorderedStyle(Workbook wb){
162    BorderStyle thin = BorderStyle.THIN;
163    short black = IndexedColors.GREY_50_PERCENT.getIndex();
164
165    CellStyle style = wb.createCellStyle();
166    style.setBorderRight(thin);
167    style.setRightBorderColor(black);
168    style.setBorderBottom(thin);
169    style.setBottomBorderColor(black);
170    style.setBorderLeft(thin);
171    style.setLeftBorderColor(black);
172    style.setBorderTop(thin);
173    style.setTopBorderColor(black);
174    return style;
175  }
176
177  protected void addCell(Row row, int pos, String content) {
178    addCell(row, pos, content, styles.get("body"));
179  }
180
181  protected void addCell(Row row, int pos, boolean b) {
182    addCell(row, pos, b ? "Y" : "");
183  }
184
185  protected void addCell(Row row, int pos, int content) {
186    addCell(row, pos, Integer.toString(content));
187  }
188
189  protected void addCell(Row row, int pos, String content, CellStyle style) {
190    Cell cell = row.createCell(pos);
191    cell.setCellValue(content);
192    cell.setCellStyle(style);
193  }
194
195  protected int columnPixels(double columns) {
196    double WIDTH_FACTOR = 256;
197    double PADDING = 180;
198    return (int)Math.floor(columns*WIDTH_FACTOR + PADDING);
199  }
200
201  protected void addHeaders(Sheet sheet, String... titles) {
202    Row headerRow = sheet.createRow(sheet.getRow(0) == null ? 0 : sheet.getLastRowNum()+1);
203    for (int i = 0; i < titles.length; i++) {
204      addCell(headerRow, i, titles[i], styles.get("header"));
205    }   
206  }
207  
208  protected void addRow(Sheet sheet, String... values) {
209    Row row = sheet.createRow(sheet.getLastRowNum()+1);
210    
211    for (int i = 0; i < values.length; i++) {
212      addCell(row, i, values[i], styles.get("body"));
213    }
214  }
215
216  public void dump() {
217    wb = null;
218    styles = null;
219    dr = null;
220    sheetNames = null;
221  }
222}