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    name = fixSheetNameChars(name);
104    String s = name;
105    if (sheetNames.containsKey(s)) {
106      int i = 1;
107      do {
108        i++;
109        s = name+" "+Integer.toString(i);
110      } while (sheetNames.containsKey(s));
111    }
112    Sheet res = wb.createSheet(s); 
113    sheetNames.put(s, res);
114    return res;
115  }
116
117  private String fixSheetNameChars(String name) {
118    StringBuilder b = new StringBuilder();
119    for (char ch : name.toCharArray()) {
120      switch (ch) {
121        case '/':
122        case '\\':
123        case '?':
124        case '*':
125        case ']':
126        case '[':
127        case ':':
128          b.append('_');
129          break;
130        default:
131          b.append(ch);
132      }
133    }
134    return b.toString();
135  }
136
137  private static Map<String, CellStyle> createStyles(Workbook wb){
138    Map<String, CellStyle> styles = new HashMap<>();
139
140    CellStyle style;
141    Font headerFont = wb.createFont();
142    headerFont.setBold(true);
143    style = createBorderedStyle(wb);
144    style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
145    style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
146    style.setVerticalAlignment(VerticalAlignment.TOP);
147    style.setWrapText(true);
148    style.setFont(headerFont);
149    styles.put("header", style);
150
151    style = createBorderedStyle(wb);
152    style.setVerticalAlignment(VerticalAlignment.TOP);
153    style.setWrapText(true);    
154    styles.put("body", style);
155
156    return styles;
157  }
158
159  private static CellStyle createBorderedStyle(Workbook wb){
160    BorderStyle thin = BorderStyle.THIN;
161    short black = IndexedColors.GREY_50_PERCENT.getIndex();
162
163    CellStyle style = wb.createCellStyle();
164    style.setBorderRight(thin);
165    style.setRightBorderColor(black);
166    style.setBorderBottom(thin);
167    style.setBottomBorderColor(black);
168    style.setBorderLeft(thin);
169    style.setLeftBorderColor(black);
170    style.setBorderTop(thin);
171    style.setTopBorderColor(black);
172    return style;
173  }
174
175  protected void addCell(Row row, int pos, String content) {
176    addCell(row, pos, content, styles.get("body"));
177  }
178
179  protected void addCell(Row row, int pos, boolean b) {
180    addCell(row, pos, b ? "Y" : "");
181  }
182
183  protected void addCell(Row row, int pos, int content) {
184    addCell(row, pos, Integer.toString(content));
185  }
186
187  protected void addCell(Row row, int pos, String content, CellStyle style) {
188    Cell cell = row.createCell(pos);
189    cell.setCellValue(content);
190    cell.setCellStyle(style);
191  }
192
193  protected int columnPixels(double columns) {
194    double WIDTH_FACTOR = 256;
195    double PADDING = 180;
196    return (int)Math.floor(columns*WIDTH_FACTOR + PADDING);
197  }
198
199  protected void addHeaders(Sheet sheet, String... titles) {
200    Row headerRow = sheet.createRow(sheet.getRow(0) == null ? 0 : sheet.getLastRowNum()+1);
201    for (int i = 0; i < titles.length; i++) {
202      addCell(headerRow, i, titles[i], styles.get("header"));
203    }   
204  }
205  
206  protected void addRow(Sheet sheet, String... values) {
207    Row row = sheet.createRow(sheet.getLastRowNum()+1);
208    
209    for (int i = 0; i < values.length; i++) {
210      addCell(row, i, values[i], styles.get("body"));
211    }
212  }
213  
214}