
001package org.hl7.fhir.r5.testfactory.dataprovider; 002 003import org.apache.poi.ss.usermodel.*; 004import org.apache.poi.ss.util.CellReference; 005import org.hl7.fhir.exceptions.FHIRException; 006import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 007import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 008import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 009import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 010 011import java.io.FileInputStream; 012import java.io.IOException; 013import java.util.ArrayList; 014import java.util.List; 015import java.util.Locale; 016import java.util.HashMap; 017import java.util.Map; 018 019 020@MarkedToMoveToAdjunctPackage 021public class ExcelDataProvider extends TableDataProvider { 022 023 private Workbook workbook; 024 private Sheet sheet; 025 private List<String> columnHeaders; 026 private int currentRowIndex = -1; 027 private Map<String, Integer> columnIndexMap = new HashMap<>(); 028 private Row currentRow; 029 private DataFormatter df; 030 private int startRow = 0; 031 private int startCol = 0; 032 private int endRow = -1; 033 private int endCol = -1; 034 035 /** 036 * Constructs an ExcelTableDataProvider. 037 * 038 * @param filename The path to the Excel file. 039 * @param sheetName The name of the sheet to read. 040 * @param sheetName The range of the sheet to read. 041 * @throws IOException If an I/O error occurs. 042 * @throws InvalidFormatException If the file format is invalid. 043 */ 044 public ExcelDataProvider(String filename, String sheetName, String range, Locale locale) throws IOException, InvalidFormatException { 045 FileInputStream fis = new FileInputStream(ManagedFileAccess.file(filename)); 046 this.workbook = WorkbookFactory.create(fis); 047 if (sheetName != null) { 048 this.sheet = workbook.getSheet(sheetName); 049 } 050 051 df = new DataFormatter(locale); 052 if (sheet == null) { 053 List<String> names = new ArrayList<String>(); 054 for (int i = 0; i < workbook.getNumberOfSheets(); i++) { 055 names.add(workbook.getSheetName(i)); 056 } 057 if (sheetName == null && names.size() > 0) { 058 this.sheet = workbook.getSheet(names.get(0)); 059 } else { 060 throw new IllegalArgumentException("Sheet '" + sheetName + "' does not exist in the file. Sheet Names = "+CommaSeparatedStringBuilder.join(",", names)); 061 } 062 } 063 if (range != null ) { 064 String[] parts = range.split(":"); 065 CellReference startCell = new CellReference(parts[0]); 066 startRow = startCell.getRow(); 067 startCol = startCell.getCol(); 068 if (parts.length==2) { 069 CellReference endCell = new CellReference(parts[1]); 070 endRow = endCell.getRow(); 071 endCol = endCell.getCol(); 072 } 073 } 074 loadColumnHeaders(); 075 } 076 077 078 public ExcelDataProvider(String filename) throws InvalidFormatException, IOException { 079 FileInputStream fis = new FileInputStream(ManagedFileAccess.file(filename)); 080 this.workbook = WorkbookFactory.create(fis); 081 this.sheet = workbook.getSheetAt(0); 082 loadColumnHeaders(); 083 } 084 085 086 /** 087 * Loads the column headers from the first row of the sheet. 088 */ 089 private void loadColumnHeaders() { 090 columnHeaders = new ArrayList<>(); 091 columnHeaders.add("counter"); 092 Row headerRow = sheet.getRow(startRow); 093 if (headerRow != null) { 094 for (Cell cell : headerRow) { 095 if (cell.getColumnIndex()>= startCol && (endCol==-1 || cell.getColumnIndex()<= endCol )) { 096 String headerName = cell.getStringCellValue().trim(); 097 columnHeaders.add(headerName); 098 columnIndexMap.put(headerName, cell.getColumnIndex()); 099 } 100 } 101 } 102 } 103 104 @Override 105 public List<String> columns() { 106 return columnHeaders; 107 } 108 109 @Override 110 public boolean nextRow() { 111 currentRowIndex++; 112 currentRow = sheet.getRow(startRow + currentRowIndex + 1); // Skip the header row 113 if (currentRow != null && endRow!=-1 && endRow == startRow + currentRowIndex) { 114 currentRow = null; 115 } 116 return currentRow != null; 117 } 118 119 @Override 120 public List<String> cells() { 121 List<String> cellValues = new ArrayList<>(); 122 cellValues.add(""+(currentRowIndex+1)); 123 if (currentRow != null) { 124 for (Cell cell : currentRow) { 125 int i = cell.getColumnIndex(); 126 if (i>= startCol && (endCol==-1 || i<= endCol )) { 127 while (cellValues.size() <= i-startCol) { 128 cellValues.add(""); 129 } 130 cellValues.add(getCellValue(cell).trim()); 131 } 132 } 133 } 134 return cellValues; 135 } 136 137 @Override 138 public String cell(String name) { 139 if ("counter".equals(name)) { 140 return ""+currentRowIndex; 141 } else { 142 Integer columnIndex = columnIndexMap.get(name); 143 if (columnIndex == null || currentRow == null) { 144 return null; 145 } 146 Cell cell = currentRow.getCell(columnIndex); 147 return cell == null ? null : getCellValue(cell).trim(); 148 } 149 } 150 151 /** 152 * Utility method to get a cell value as a string. 153 * 154 * @param cell The cell. 155 * @return The cell value as a string. 156 */ 157 private String getCellValue(Cell cell) { 158 switch (cell.getCellType()) { 159 case STRING: 160 return cell.getStringCellValue(); 161 case NUMERIC: 162 return df.formatCellValue(cell); 163 case BOOLEAN: 164 return Boolean.toString(cell.getBooleanCellValue()); 165 case FORMULA: 166 return cell.getCellFormula(); 167 case BLANK: 168 return ""; 169 default: 170 return ""; 171 } 172 } 173 174 /** 175 * Closes the workbook and releases resources. 176 * 177 * @throws IOException If an I/O error occurs. 178 */ 179 public void close() throws IOException { 180 if (workbook != null) { 181 workbook.close(); 182 } 183 } 184 185 @Override 186 public void reset() throws FHIRException { 187 currentRowIndex = -1; 188 } 189 190}