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