src/core/net/sf/basedb/util/excel/XlsxTableWriter.java

Code
Comments
Other
Rev Date Author Line
7675 27 Mar 19 nicklas 1 package net.sf.basedb.util.excel;
7675 27 Mar 19 nicklas 2
7675 27 Mar 19 nicklas 3
7675 27 Mar 19 nicklas 4 import java.io.IOException;
7675 27 Mar 19 nicklas 5 import java.io.OutputStream;
7675 27 Mar 19 nicklas 6 import java.util.Collections;
7675 27 Mar 19 nicklas 7 import java.util.Map;
7675 27 Mar 19 nicklas 8
7675 27 Mar 19 nicklas 9 import org.apache.commons.io.output.NullWriter;
7675 27 Mar 19 nicklas 10 import org.apache.poi.ss.usermodel.Cell;
7675 27 Mar 19 nicklas 11 import org.apache.poi.ss.usermodel.Row;
7675 27 Mar 19 nicklas 12 import org.apache.poi.ss.usermodel.Sheet;
7675 27 Mar 19 nicklas 13 import org.apache.poi.ss.usermodel.Workbook;
7675 27 Mar 19 nicklas 14 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
7675 27 Mar 19 nicklas 15 import org.apache.poi.xssf.usermodel.XSSFWorkbookType;
7675 27 Mar 19 nicklas 16
7675 27 Mar 19 nicklas 17 import net.sf.basedb.util.FileUtil;
7675 27 Mar 19 nicklas 18 import net.sf.basedb.util.encode.EncoderDecoder;
7675 27 Mar 19 nicklas 19 import net.sf.basedb.util.export.TableWriter;
7675 27 Mar 19 nicklas 20 import net.sf.basedb.util.formatter.Formatter;
7675 27 Mar 19 nicklas 21
7675 27 Mar 19 nicklas 22 /**
7675 27 Mar 19 nicklas 23   This is a simple wrapper for the TableWriter class to make it
7675 27 Mar 19 nicklas 24   create an Excel workbook instead of a CSV file. There are some
7675 27 Mar 19 nicklas 25   things to consider:
7675 27 Mar 19 nicklas 26   
7675 27 Mar 19 nicklas 27   * The  {@link #tablePrintHeaders(Object...)} and {@link #tablePrintData(Object...)} 
7678 27 Mar 19 nicklas 28     (including overloaded variants) methods can be used to write a single row at
7678 27 Mar 19 nicklas 29     a time. The various print() and write() methods write to a single cell and
7678 27 Mar 19 nicklas 30     will advance one column at a time. The println() variants ends the current row 
7678 27 Mar 19 nicklas 31     and starts over on the first column.
7675 27 Mar 19 nicklas 32      
7675 27 Mar 19 nicklas 33    * Writing data to Excel is by default handled by the {@link AutoFormatter} implementation
7675 27 Mar 19 nicklas 34      which will try to infer the correct Excel data type and format string from the class
7675 27 Mar 19 nicklas 35      and value of the data.
7675 27 Mar 19 nicklas 36  
7675 27 Mar 19 nicklas 37     * It is possible to assign a different formatter implementation to a given column by
7675 27 Mar 19 nicklas 38       calling {@link #setColumnFormatter(int, Formatter)}, but note that the formatter is
7675 27 Mar 19 nicklas 39       only used by this class if it also implements the {@link ExcelFormatter} interface.
7675 27 Mar 19 nicklas 40  
7675 27 Mar 19 nicklas 41   * Call {@link #saveTo(OutputStream)} to write the Excel workbook to an output location.
7675 27 Mar 19 nicklas 42
7675 27 Mar 19 nicklas 43   @since 3.15
7675 27 Mar 19 nicklas 44 */
7675 27 Mar 19 nicklas 45 public class XlsxTableWriter 
7675 27 Mar 19 nicklas 46   extends TableWriter
7675 27 Mar 19 nicklas 47 {
7675 27 Mar 19 nicklas 48
7675 27 Mar 19 nicklas 49   private final String name;
7675 27 Mar 19 nicklas 50   private final Workbook workbook;
7675 27 Mar 19 nicklas 51   private final Sheet sheet;
7675 27 Mar 19 nicklas 52   private final boolean externalWorkbook; // TRUE if the workbook was supplied externally
7675 27 Mar 19 nicklas 53   private final CellStyleCreator styleCreator;
7675 27 Mar 19 nicklas 54   private final AutoFormatter autoFormatter;
7675 27 Mar 19 nicklas 55   
7675 27 Mar 19 nicklas 56   private int rowNo;
7675 27 Mar 19 nicklas 57   private Row currentRow;
7678 27 Mar 19 nicklas 58   private int colNo;
7675 27 Mar 19 nicklas 59   
7675 27 Mar 19 nicklas 60   /**
7675 27 Mar 19 nicklas 61     Create a new workbook with a single worksheet.
7675 27 Mar 19 nicklas 62     @param name The name of the worksheet
7675 27 Mar 19 nicklas 63   */
7675 27 Mar 19 nicklas 64   public XlsxTableWriter(String name)
7675 27 Mar 19 nicklas 65   {
7675 27 Mar 19 nicklas 66     this(null, name);
7675 27 Mar 19 nicklas 67   }
7675 27 Mar 19 nicklas 68
7675 27 Mar 19 nicklas 69   /**
7675 27 Mar 19 nicklas 70     Add a sheet to the given Excel workbook.
7675 27 Mar 19 nicklas 71     @param name The name of the worksheet
7675 27 Mar 19 nicklas 72   */
7675 27 Mar 19 nicklas 73   public XlsxTableWriter(Workbook workbook, String name)
7675 27 Mar 19 nicklas 74   {
7675 27 Mar 19 nicklas 75     super(new NullWriter());
7675 27 Mar 19 nicklas 76     this.name = name;
7675 27 Mar 19 nicklas 77     this.externalWorkbook = workbook != null;
7675 27 Mar 19 nicklas 78     if (workbook == null) workbook = new XSSFWorkbook(XSSFWorkbookType.XLSX);
7675 27 Mar 19 nicklas 79     this.workbook = workbook; 
7675 27 Mar 19 nicklas 80     this.sheet = workbook.createSheet(name);
7675 27 Mar 19 nicklas 81     this.styleCreator = new CellStyleCreator(workbook);
7675 27 Mar 19 nicklas 82     this.autoFormatter = new AutoFormatter();
7675 27 Mar 19 nicklas 83   }
7675 27 Mar 19 nicklas 84   
7675 27 Mar 19 nicklas 85   /**
7675 27 Mar 19 nicklas 86     Get the workbook.
7675 27 Mar 19 nicklas 87   */
7675 27 Mar 19 nicklas 88   public Workbook getWorkbook()
7675 27 Mar 19 nicklas 89   {
7675 27 Mar 19 nicklas 90     return workbook;
7675 27 Mar 19 nicklas 91   }
7675 27 Mar 19 nicklas 92   
7675 27 Mar 19 nicklas 93   /**
7675 27 Mar 19 nicklas 94     Get the current sheet in the workbook.
7675 27 Mar 19 nicklas 95   */
7675 27 Mar 19 nicklas 96   public Sheet getSheet()
7675 27 Mar 19 nicklas 97   {
7675 27 Mar 19 nicklas 98     return sheet;
7675 27 Mar 19 nicklas 99   }
7675 27 Mar 19 nicklas 100
7675 27 Mar 19 nicklas 101   /**
7675 27 Mar 19 nicklas 102     Get row that was the last one to be printed.
7675 27 Mar 19 nicklas 103   */
7675 27 Mar 19 nicklas 104   public Row getCurrentRow()
7675 27 Mar 19 nicklas 105   {
7675 27 Mar 19 nicklas 106     return currentRow;
7675 27 Mar 19 nicklas 107   }
7675 27 Mar 19 nicklas 108   
7675 27 Mar 19 nicklas 109   /**
7675 27 Mar 19 nicklas 110     Get the auto-formatter instance to use for columns that has no 
7675 27 Mar 19 nicklas 111     specified formatter via {@link #setColumnFormatter(int, Formatter)}.
7675 27 Mar 19 nicklas 112   */
7675 27 Mar 19 nicklas 113   public AutoFormatter getAutoFormatter()
7675 27 Mar 19 nicklas 114   {
7675 27 Mar 19 nicklas 115     return autoFormatter;
7675 27 Mar 19 nicklas 116   }
7675 27 Mar 19 nicklas 117   
7675 27 Mar 19 nicklas 118   /**
7675 27 Mar 19 nicklas 119     Get the style creator that can be used for custom styling of cells.
7675 27 Mar 19 nicklas 120   */
7675 27 Mar 19 nicklas 121   public CellStyleCreator getCellStyleCreator()
7675 27 Mar 19 nicklas 122   {
7675 27 Mar 19 nicklas 123     return styleCreator;
7675 27 Mar 19 nicklas 124   }
7675 27 Mar 19 nicklas 125   
7675 27 Mar 19 nicklas 126   @Override
7675 27 Mar 19 nicklas 127   public void tablePrintHeaders(Object... headers)
7675 27 Mar 19 nicklas 128   {
7675 27 Mar 19 nicklas 129     super.tablePrintHeaders(headers);
7675 27 Mar 19 nicklas 130     // Let the default implementation set the values and then we can try to adjust column widths
7675 27 Mar 19 nicklas 131     // to match the text length
7675 27 Mar 19 nicklas 132     for (int col = 0; col < headers.length; col++)
7675 27 Mar 19 nicklas 133     {
7675 27 Mar 19 nicklas 134       Cell cell = currentRow.getCell(col);
7675 27 Mar 19 nicklas 135       if (cell == null) continue;
7675 27 Mar 19 nicklas 136       String headerText = cell.getStringCellValue();
7675 27 Mar 19 nicklas 137       if (headerText == null) continue;
7675 27 Mar 19 nicklas 138       
7675 27 Mar 19 nicklas 139       // Set column width based on title length + margin --  but not less than 10 or wider than 50
7675 27 Mar 19 nicklas 140       int colWidth = 2 + Math.min(Math.max(8, headerText.length()), 48);
7675 27 Mar 19 nicklas 141       sheet.setColumnWidth(col, colWidth*256);
7675 27 Mar 19 nicklas 142     }
7675 27 Mar 19 nicklas 143   }
7675 27 Mar 19 nicklas 144
7675 27 Mar 19 nicklas 145   @SuppressWarnings({ "rawtypes", "unchecked" })
7675 27 Mar 19 nicklas 146   @Override
7675 27 Mar 19 nicklas 147   public void tablePrintData(EncoderDecoder encoder, Map<Integer, Formatter<?>> formatters, Object... data) 
7675 27 Mar 19 nicklas 148   {
7675 27 Mar 19 nicklas 149     currentRow = sheet.createRow(rowNo++);
7678 27 Mar 19 nicklas 150     colNo = 0;
7675 27 Mar 19 nicklas 151     String nv = getNullValue();
7675 27 Mar 19 nicklas 152     if (formatters == null) formatters = Collections.emptyMap();
7675 27 Mar 19 nicklas 153     
7675 27 Mar 19 nicklas 154     for (int col = 0; col < data.length; col++)
7675 27 Mar 19 nicklas 155     {
7675 27 Mar 19 nicklas 156       // Convert and format data value to ExcelValue
7675 27 Mar 19 nicklas 157       Formatter fmt = formatters.get(col);
7675 27 Mar 19 nicklas 158       ExcelFormatter excelFmt = (fmt instanceof ExcelFormatter) ? (ExcelFormatter)fmt : autoFormatter;
7675 27 Mar 19 nicklas 159       ExcelValue ev = excelFmt.toExcelValue(data[col]);
7675 27 Mar 19 nicklas 160       Cell cell = currentRow.createCell(col);
7675 27 Mar 19 nicklas 161       ev.writeTo(cell, styleCreator);
7675 27 Mar 19 nicklas 162     }
7675 27 Mar 19 nicklas 163   }
7675 27 Mar 19 nicklas 164
7675 27 Mar 19 nicklas 165   /**
7675 27 Mar 19 nicklas 166     Closes the workbook unless it was supplied in the constructor from an
7675 27 Mar 19 nicklas 167     external source.
7675 27 Mar 19 nicklas 168   */
7675 27 Mar 19 nicklas 169   @Override
7675 27 Mar 19 nicklas 170   public void close()
7675 27 Mar 19 nicklas 171   {
7675 27 Mar 19 nicklas 172     super.close();
7675 27 Mar 19 nicklas 173     if (!externalWorkbook) FileUtil.close(workbook);
7675 27 Mar 19 nicklas 174   }
7675 27 Mar 19 nicklas 175   
7675 27 Mar 19 nicklas 176   /**
7675 27 Mar 19 nicklas 177     Save the workbook to the given stream.
7675 27 Mar 19 nicklas 178   */
7675 27 Mar 19 nicklas 179   public void saveTo(OutputStream out)
7675 27 Mar 19 nicklas 180     throws IOException
7675 27 Mar 19 nicklas 181   {
7675 27 Mar 19 nicklas 182     workbook.write(out);
7675 27 Mar 19 nicklas 183   }
7675 27 Mar 19 nicklas 184   
7678 27 Mar 19 nicklas 185   @Override
7678 27 Mar 19 nicklas 186   public void write(int c) 
7678 27 Mar 19 nicklas 187   {
7678 27 Mar 19 nicklas 188     writeData(c, false);
7678 27 Mar 19 nicklas 189   }
7678 27 Mar 19 nicklas 190
7678 27 Mar 19 nicklas 191   @Override
7678 27 Mar 19 nicklas 192   public void write(char[] buf, int off, int len) 
7678 27 Mar 19 nicklas 193   {
7678 27 Mar 19 nicklas 194     writeData(String.copyValueOf(buf, off, len), false);
7678 27 Mar 19 nicklas 195   }
7678 27 Mar 19 nicklas 196
7678 27 Mar 19 nicklas 197   @Override
7678 27 Mar 19 nicklas 198   public void write(char[] buf) 
7678 27 Mar 19 nicklas 199   {
7678 27 Mar 19 nicklas 200     write(buf, 0, buf.length);
7678 27 Mar 19 nicklas 201   }
7678 27 Mar 19 nicklas 202
7678 27 Mar 19 nicklas 203   @Override
7678 27 Mar 19 nicklas 204   public void write(String s, int off, int len) 
7678 27 Mar 19 nicklas 205   {
7678 27 Mar 19 nicklas 206     writeData(s.substring(off, off+len), false);
7678 27 Mar 19 nicklas 207   }
7678 27 Mar 19 nicklas 208
7678 27 Mar 19 nicklas 209   @Override
7678 27 Mar 19 nicklas 210   public void write(String s) 
7678 27 Mar 19 nicklas 211   {
7678 27 Mar 19 nicklas 212     writeData(s, false);
7678 27 Mar 19 nicklas 213   }
7678 27 Mar 19 nicklas 214
7678 27 Mar 19 nicklas 215   @Override
7678 27 Mar 19 nicklas 216   public void print(boolean b) 
7678 27 Mar 19 nicklas 217   {
7678 27 Mar 19 nicklas 218     writeData(b, false);
7678 27 Mar 19 nicklas 219   }
7678 27 Mar 19 nicklas 220
7678 27 Mar 19 nicklas 221   @Override
7678 27 Mar 19 nicklas 222   public void print(char c) 
7678 27 Mar 19 nicklas 223   {
7678 27 Mar 19 nicklas 224     writeData(c, false);
7678 27 Mar 19 nicklas 225   }
7678 27 Mar 19 nicklas 226
7678 27 Mar 19 nicklas 227   @Override
7678 27 Mar 19 nicklas 228   public void print(int i) 
7678 27 Mar 19 nicklas 229   {
7678 27 Mar 19 nicklas 230     writeData(i, false);
7678 27 Mar 19 nicklas 231   }
7678 27 Mar 19 nicklas 232
7678 27 Mar 19 nicklas 233   @Override
7678 27 Mar 19 nicklas 234   public void print(long l) 
7678 27 Mar 19 nicklas 235   {
7678 27 Mar 19 nicklas 236     writeData(l, false);
7678 27 Mar 19 nicklas 237   }
7678 27 Mar 19 nicklas 238
7678 27 Mar 19 nicklas 239   @Override
7678 27 Mar 19 nicklas 240   public void print(float f) 
7678 27 Mar 19 nicklas 241   {
7678 27 Mar 19 nicklas 242     writeData(f, false);
7678 27 Mar 19 nicklas 243   }
7678 27 Mar 19 nicklas 244
7678 27 Mar 19 nicklas 245   @Override
7678 27 Mar 19 nicklas 246   public void print(double d) 
7678 27 Mar 19 nicklas 247   {
7678 27 Mar 19 nicklas 248     writeData(d, false);
7678 27 Mar 19 nicklas 249   }
7678 27 Mar 19 nicklas 250
7678 27 Mar 19 nicklas 251   @Override
7678 27 Mar 19 nicklas 252   public void print(char[] s) 
7678 27 Mar 19 nicklas 253   {
7678 27 Mar 19 nicklas 254     writeData(String.copyValueOf(s), false);
7678 27 Mar 19 nicklas 255   }
7678 27 Mar 19 nicklas 256
7678 27 Mar 19 nicklas 257   @Override
7678 27 Mar 19 nicklas 258   public void print(String s) 
7678 27 Mar 19 nicklas 259   {
7678 27 Mar 19 nicklas 260     writeData(s, false);
7678 27 Mar 19 nicklas 261   }
7678 27 Mar 19 nicklas 262
7678 27 Mar 19 nicklas 263   @Override
7678 27 Mar 19 nicklas 264   public void print(Object obj) 
7678 27 Mar 19 nicklas 265   {
7678 27 Mar 19 nicklas 266     writeData(obj, false);
7678 27 Mar 19 nicklas 267   }
7678 27 Mar 19 nicklas 268
7678 27 Mar 19 nicklas 269   @Override
7678 27 Mar 19 nicklas 270   public void println() 
7678 27 Mar 19 nicklas 271   {
7678 27 Mar 19 nicklas 272     if (colNo == 0) rowNo++;
7678 27 Mar 19 nicklas 273     colNo = 0;
7678 27 Mar 19 nicklas 274   }
7678 27 Mar 19 nicklas 275
7678 27 Mar 19 nicklas 276   @Override
7678 27 Mar 19 nicklas 277   public void println(boolean b) 
7678 27 Mar 19 nicklas 278   {
7678 27 Mar 19 nicklas 279     writeData(b, true);
7678 27 Mar 19 nicklas 280   }
7678 27 Mar 19 nicklas 281
7678 27 Mar 19 nicklas 282   @Override
7678 27 Mar 19 nicklas 283   public void println(char c) 
7678 27 Mar 19 nicklas 284   {
7678 27 Mar 19 nicklas 285     writeData(c, true);
7678 27 Mar 19 nicklas 286   }
7678 27 Mar 19 nicklas 287
7678 27 Mar 19 nicklas 288   @Override
7678 27 Mar 19 nicklas 289   public void println(int i) 
7678 27 Mar 19 nicklas 290   {
7678 27 Mar 19 nicklas 291     writeData(i, true);
7678 27 Mar 19 nicklas 292   }
7678 27 Mar 19 nicklas 293
7678 27 Mar 19 nicklas 294   @Override
7678 27 Mar 19 nicklas 295   public void println(long l) 
7678 27 Mar 19 nicklas 296   {
7678 27 Mar 19 nicklas 297     writeData(l, true);
7678 27 Mar 19 nicklas 298   }
7678 27 Mar 19 nicklas 299
7678 27 Mar 19 nicklas 300   @Override
7678 27 Mar 19 nicklas 301   public void println(float f) 
7678 27 Mar 19 nicklas 302   {
7678 27 Mar 19 nicklas 303     writeData(f, true);
7678 27 Mar 19 nicklas 304   }
7678 27 Mar 19 nicklas 305
7678 27 Mar 19 nicklas 306   @Override
7678 27 Mar 19 nicklas 307   public void println(double d) 
7678 27 Mar 19 nicklas 308   {
7678 27 Mar 19 nicklas 309     writeData(d, true);
7678 27 Mar 19 nicklas 310   }
7678 27 Mar 19 nicklas 311
7678 27 Mar 19 nicklas 312   @Override
7678 27 Mar 19 nicklas 313   public void println(char[] s) 
7678 27 Mar 19 nicklas 314   {
7678 27 Mar 19 nicklas 315     writeData(String.copyValueOf(s), true);
7678 27 Mar 19 nicklas 316   }
7678 27 Mar 19 nicklas 317
7678 27 Mar 19 nicklas 318   @Override
7678 27 Mar 19 nicklas 319   public void println(String s)
7678 27 Mar 19 nicklas 320   {
7678 27 Mar 19 nicklas 321     writeData(s, true);
7678 27 Mar 19 nicklas 322   }
7678 27 Mar 19 nicklas 323
7678 27 Mar 19 nicklas 324   @Override
7678 27 Mar 19 nicklas 325   public void println(Object o) 
7678 27 Mar 19 nicklas 326   {
7678 27 Mar 19 nicklas 327     writeData(o, true);
7678 27 Mar 19 nicklas 328   }
7678 27 Mar 19 nicklas 329
7678 27 Mar 19 nicklas 330   /**
7678 27 Mar 19 nicklas 331     Write the data to the next cell. If colNo=0 a new row is created, 
7678 27 Mar 19 nicklas 332     otherwise the next cell in the current row is used. If
7678 27 Mar 19 nicklas 333     'endOfRow' the current row is ended (by setting colNo to 0) so
7678 27 Mar 19 nicklas 334     that a new row is created the next time this method is called.
7678 27 Mar 19 nicklas 335   */
7678 27 Mar 19 nicklas 336   @SuppressWarnings({ "rawtypes", "unchecked" })
7678 27 Mar 19 nicklas 337   private void writeData(Object data, boolean endOfRow)
7678 27 Mar 19 nicklas 338   {
7678 27 Mar 19 nicklas 339     if (colNo == 0) currentRow = sheet.createRow(rowNo++);
7678 27 Mar 19 nicklas 340     Formatter fmt = getColumnFormatter(colNo);
7678 27 Mar 19 nicklas 341     ExcelFormatter excelFmt = (fmt instanceof ExcelFormatter) ? (ExcelFormatter)fmt : autoFormatter;
7678 27 Mar 19 nicklas 342     ExcelValue ev = excelFmt.toExcelValue(data);
7678 27 Mar 19 nicklas 343     Cell cell = currentRow.createCell(colNo++);
7678 27 Mar 19 nicklas 344     ev.writeTo(cell, styleCreator);
7678 27 Mar 19 nicklas 345     if (endOfRow) colNo = 0;
7678 27 Mar 19 nicklas 346   }
7678 27 Mar 19 nicklas 347   
7675 27 Mar 19 nicklas 348 }