package com.revolsys.record.io.format.xlsx;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import javax.xml.bind.JAXBException;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.io3.Save;
import org.docx4j.openpackaging.packages.SpreadsheetMLPackage;
import org.docx4j.openpackaging.parts.DocPropsCustomPart;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.SpreadsheetML.TablePart;
import org.docx4j.openpackaging.parts.SpreadsheetML.WorksheetPart;
import org.xlsx4j.jaxb.Context;
import org.xlsx4j.sml.CTAutoFilter;
import org.xlsx4j.sml.CTRst;
import org.xlsx4j.sml.CTTable;
import org.xlsx4j.sml.CTTableColumn;
import org.xlsx4j.sml.CTTableColumns;
import org.xlsx4j.sml.CTTablePart;
import org.xlsx4j.sml.CTTableParts;
import org.xlsx4j.sml.CTTableStyleInfo;
import org.xlsx4j.sml.CTXstringWhitespace;
import org.xlsx4j.sml.Cell;
import org.xlsx4j.sml.Col;
import org.xlsx4j.sml.Cols;
import org.xlsx4j.sml.ObjectFactory;
import org.xlsx4j.sml.Row;
import org.xlsx4j.sml.STCellType;
import org.xlsx4j.sml.SheetData;
import org.xlsx4j.sml.Worksheet;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.io.AbstractRecordWriter;
import com.revolsys.io.FileUtil;
import com.revolsys.record.Record;
import com.revolsys.record.schema.FieldDefinition;
import com.revolsys.record.schema.RecordDefinition;
import com.revolsys.spring.resource.Resource;
import com.revolsys.util.Exceptions;
import com.revolsys.util.number.Doubles;
public class XlsxRecordWriter extends AbstractRecordWriter {
private static final ObjectFactory smlObjectFactory = Context.getsmlObjectFactory();
public static String getRef(long columnIndex, final int rowIndex) {
columnIndex--;
final StringBuilder ref = new StringBuilder();
do {
final long index = columnIndex % 26;
ref.append((char)('A' + index));
columnIndex = columnIndex / 26;
} while (columnIndex > 0);
ref.append(rowIndex);
return ref.toString();
}
private OutputStream out;
private final RecordDefinition recordDefinition;
private SpreadsheetMLPackage spreadsheetPackage;
private WorksheetPart sheet;
private SheetData sheetData;
private List<Row> sheetRows = Collections.emptyList();
public XlsxRecordWriter(final RecordDefinition recordDefinition, final OutputStream out) {
try {
this.recordDefinition = recordDefinition;
this.out = new BufferedOutputStream(out);
this.spreadsheetPackage = SpreadsheetMLPackage.createPackage();
final GeometryFactory geometryFactory = recordDefinition.getGeometryFactory();
if (geometryFactory != null) {
final int coordinateSystemId = geometryFactory.getCoordinateSystemId();
if (coordinateSystemId > 0) {
this.spreadsheetPackage.addDocPropsCustomPart();
final DocPropsCustomPart customProperties = this.spreadsheetPackage
.getDocPropsCustomPart();
customProperties.setProperty("srid", Integer.toString(coordinateSystemId));
final int axisCount = geometryFactory.getAxisCount();
customProperties.setProperty("axisCount", Integer.toString(axisCount));
final double scaleXY = geometryFactory.getScaleXY();
if (scaleXY > 0) {
customProperties.setProperty("scaleXy", Doubles.toString(scaleXY));
}
if (axisCount > 2) {
final double scaleZ = geometryFactory.getScaleZ();
if (scaleZ > 0) {
customProperties.setProperty("scaleZ", Doubles.toString(scaleZ));
}
}
}
}
String name = recordDefinition.getName();
if (name.length() > 30) {
name = name.substring(0, 30);
}
final PartName spreadsheetPartName = new PartName("/xl/worksheets/sheet1.xml");
this.sheet = this.spreadsheetPackage.createWorksheetPart(spreadsheetPartName, name, 1);
final Worksheet worksheet = this.sheet.getContents();
final CTTableParts tableParts = smlObjectFactory.createCTTableParts();
tableParts.setCount(1L);
final CTTablePart tablePart = smlObjectFactory.createCTTablePart();
tablePart.setId("rId1");
tableParts.getTablePart().add(tablePart);
worksheet.setTableParts(tableParts);
this.sheetData = worksheet.getSheetData();
this.sheetRows = this.sheetData.getRow();
addHeaderRow(worksheet, recordDefinition);
} catch (final Docx4JException | JAXBException e) {
throw Exceptions.wrap(e);
}
}
public XlsxRecordWriter(final RecordDefinition recordDefinition, final Resource resource) {
this(recordDefinition, resource.newOutputStream());
setResource(resource);
}
private void addCellInlineString(final List<Cell> cells, String value) {
if (value == null) {
value = "";
}
final CTXstringWhitespace cellContext = smlObjectFactory.createCTXstringWhitespace();
cellContext.setValue(value);
final CTRst cellString = new CTRst();
cellString.setT(cellContext);
final Cell cell = smlObjectFactory.createCell();
cell.setT(STCellType.INLINE_STR);
cell.setIs(cellString);
cells.add(cell);
}
private void addCellNumber(final List<Cell> cells, final String value) {
final Cell cell = smlObjectFactory.createCell();
cell.setV(value);
cells.add(cell);
}
private void addHeaderRow(final Worksheet worksheet, final RecordDefinition recordDefinition) {
final List<Cols> columnGroups = worksheet.getCols();
final Cols columns = smlObjectFactory.createCols();
columnGroups.add(columns);
final Row headerRow = smlObjectFactory.createRow();
this.sheetRows.add(headerRow);
final List<Cell> cells = headerRow.getC();
for (final FieldDefinition field : recordDefinition.getFields()) {
final String fieldName = field.getName();
final Col column = smlObjectFactory.createCol();
columns.getCol().add(column);
column.setMin(field.getIndex() + 1);
column.setMax(field.getIndex() + 1);
column.setBestFit(true);
final int textLength = Math.min(40,
Math.max(fieldName.length() + 2, field.getMaxStringLength()));
column.setWidth(textLength * 1.25);
addCellInlineString(cells, fieldName);
}
}
/**
* Closes the underlying reader.
*/
@Override
public synchronized void close() {
if (this.out != null) {
try {
final long fieldCount = this.recordDefinition.getFieldCount();
final String ref = "A1:" + getRef(fieldCount, this.sheetRows.size());
final TablePart tablePart = new TablePart();
this.spreadsheetPackage.addTargetPart(tablePart);
final CTTable table = smlObjectFactory.createCTTable();
tablePart.setContents(table);
table.setId(1);
table.setName("Table1");
table.setDisplayName(this.recordDefinition.getName());
table.setRef(ref);
final CTAutoFilter autoFilter = smlObjectFactory.createCTAutoFilter();
autoFilter.setRef(ref);
table.setAutoFilter(autoFilter);
long columnIndex = 1;
final CTTableColumns tableColumns = smlObjectFactory.createCTTableColumns();
tableColumns.setCount(fieldCount);
table.setTableColumns(tableColumns);
final List<CTTableColumn> columns = tableColumns.getTableColumn();
for (final String fieldName : this.recordDefinition.getFieldNames()) {
final CTTableColumn column = smlObjectFactory.createCTTableColumn();
column.setId(columnIndex);
column.setName(fieldName);
columns.add(column);
columnIndex++;
}
final CTTableStyleInfo tableStyleInfo = smlObjectFactory.createCTTableStyleInfo();
table.setTableStyleInfo(tableStyleInfo);
tableStyleInfo.setName("TableStyleMedium14");
tableStyleInfo.setShowFirstColumn(false);
tableStyleInfo.setShowLastColumn(false);
tableStyleInfo.setShowRowStripes(true);
tableStyleInfo.setShowColumnStripes(false);
this.sheet.addTargetPart(tablePart, "rId1");
final Save save = new Save(this.spreadsheetPackage);
save.save(this.out);
try {
this.out.flush();
} catch (final IOException e) {
}
} catch (final Docx4JException e) {
throw Exceptions.wrap(e);
} finally {
FileUtil.closeSilent(this.out);
this.out = null;
this.sheet = null;
this.sheetData = null;
this.spreadsheetPackage = null;
this.sheetRows = Collections.emptyList();
}
}
}
@Override
public void flush() {
}
@Override
public RecordDefinition getRecordDefinition() {
return this.recordDefinition;
}
@Override
public void write(final Record record) {
final Row recordRow = smlObjectFactory.createRow();
this.sheetRows.add(recordRow);
final List<Cell> cells = recordRow.getC();
for (final FieldDefinition field : this.recordDefinition.getFields()) {
final Object value = record.getValue(field);
final String string = field.toString(value);
if (value instanceof Number) {
addCellNumber(cells, string);
} else {
addCellInlineString(cells, string);
}
}
}
}