Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions morf-excel/pom.xml
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,13 @@
<artifactId>morf-core</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.jexcelapi</groupId>
<artifactId>jxl</artifactId>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<dependency>
<groupId>org.alfasoftware</groupId>
Expand Down
4 changes: 2 additions & 2 deletions morf-excel/src/main/java/org/alfasoftware/morf/excel/AdditionalSchemaData.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public interface AdditionalSchemaData {
* @param columnName The column name.
* @return The column documentation.
*/
public String columnDocumentation(Table table, String columnName);
String columnDocumentation(Table table, String columnName);

/**
* Fetches the default value for a column.
Expand All @@ -40,5 +40,5 @@ public interface AdditionalSchemaData {
* @param columnName The column name.
* @return The column's default value.
*/
public String columnDefaultValue(Table table, String columnName);
String columnDefaultValue(Table table, String columnName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@

package org.alfasoftware.morf.excel;

import org.apache.commons.lang3.StringUtils;

import org.alfasoftware.morf.metadata.Table;
import org.apache.commons.lang3.StringUtils;

/**
* A default implementation of {@link AdditionalSchemaData} which provides blank
Expand Down
262 changes: 113 additions & 149 deletions morf-excel/src/main/java/org/alfasoftware/morf/excel/SpreadsheetDataSetConsumer.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import jxl.Workbook;
import jxl.format.Alignment;
import jxl.format.Colour;
import jxl.format.UnderlineStyle;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableHyperlink;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.WorkbookUtil;

/**
* Consumes data sets and outputs spreadsheets.
Expand All @@ -49,214 +49,178 @@ public class SpreadsheetDataSetConsumer implements DataSetConsumer {

private static final Log log = LogFactory.getLog(SpreadsheetDataSetConsumer.class);

/**
*
* The number of rows in the title.
*/
private static final int NUMBER_OF_ROWS_IN_TITLE = 2;

/**
* The number of rows to include in the sample section if not specified.
*/
private static final int DEFAULT_SAMPLE_ROWS = 5;

/**
* Stream to which the spreadsheet will be output.
*/
private final OutputStream documentOutputStream;

/**
* The workbook currently under construction.
*/
private WritableWorkbook workbook;

/**
* Number of rows to output for each table.
*/
private Workbook workbook;
private ConsumerStyles consumerStyles;
private final Optional<Map<String, Integer>> rowsPerTable;


/**
* Outputter for putting tables into Excel.
*/
private final TableOutputter tableOutputter;


/**
* @param documentOutputStream Stream to which the spreadsheet file should be written.
*/
public SpreadsheetDataSetConsumer(OutputStream documentOutputStream) {
this(documentOutputStream, Optional.<Map<String, Integer>>empty());
}


/**
* @param documentOutputStream Stream to which the spreadsheet file should be written.
* @param rowsPerTable stream The list of tables to export along with the number of rows per table to export. If not supplied,
* all tables are exported to the limit of rows in Excel.
*/
public SpreadsheetDataSetConsumer(
OutputStream documentOutputStream,
Optional<Map<String, Integer>> rowsPerTable) {
public SpreadsheetDataSetConsumer(OutputStream documentOutputStream, Optional<Map<String, Integer>> rowsPerTable) {
this(documentOutputStream, rowsPerTable, new DefaultAdditionalSchemaDataImpl());
}


/**
* @param documentOutputStream Stream to which the spreadsheet file should be written.
* @param rowsPerTable stream The list of tables to export along with the number of rows per table to export. If not supplied,
* all tables are exported to the limit of rows in Excel.
* @param additionalSchemaData the source of additional metadata not available from the database schema.
*/
public SpreadsheetDataSetConsumer(
OutputStream documentOutputStream,
Optional<Map<String, Integer>> rowsPerTable,
public SpreadsheetDataSetConsumer(OutputStream documentOutputStream, Optional<Map<String, Integer>> rowsPerTable,
AdditionalSchemaData additionalSchemaData) {
this(documentOutputStream, rowsPerTable, new TableOutputter(additionalSchemaData));
}


/**
* Package private constructor, for testing purposes (isolates the {@link TableOutputter}
* dependency).
*/
SpreadsheetDataSetConsumer(
OutputStream documentOutputStream,
Optional<Map<String, Integer>> rowsPerTable,
SpreadsheetDataSetConsumer(OutputStream documentOutputStream, Optional<Map<String, Integer>> rowsPerTable,
TableOutputter tableOutputter) {
super();
this.documentOutputStream = documentOutputStream;
this.tableOutputter = tableOutputter;
this.rowsPerTable = rowsPerTable;
}


/**
* @see org.alfasoftware.morf.dataset.DataSetConsumer#open()
*/
@Override
public void open() {
try {
workbook = Workbook.createWorkbook(documentOutputStream);
} catch (IOException e) {
throw new RuntimeException("Error creating writable workbook", e);
}
workbook = new HSSFWorkbook();
consumerStyles = new ConsumerStyles(workbook);
}


/**
* Create the index worksheet.
*
* <p>This also creates links back to the index in each of the worksheets.</p>
*/
public void createIndex() {
WritableSheet sheet = workbook.createSheet(spreadsheetifyName("Index"), 0);
Sheet sheet = workbook.createSheet(safeSheetName(spreadsheetifyName("Index")));
workbook.setSheetOrder(sheet.getSheetName(), 0);
createTitle(sheet, "Index");

try {
// Create links for each worksheet, apart from the first sheet which is the
// index we're currently creating
final String[] names = workbook.getSheetNames();
for (int currentSheet = 1; currentSheet < names.length; currentSheet++) {
// Create the link from the index to the table's worksheet
WritableHyperlink link = new WritableHyperlink(0, currentSheet - 1 + NUMBER_OF_ROWS_IN_TITLE, names[currentSheet], workbook.getSheet(currentSheet), 0, 0);
sheet.addHyperlink(link);

//Add the filename in column B (stored in cell B2 of each sheet)
String fileName = workbook.getSheet(currentSheet).getCell(1, 1).getContents();
Label fileNameLabel = new Label(1, currentSheet - 1 + NUMBER_OF_ROWS_IN_TITLE, fileName);
WritableFont fileNameFont = new WritableFont(WritableFont.ARIAL,10,WritableFont.NO_BOLD,false,UnderlineStyle.NO_UNDERLINE,Colour.BLACK);
WritableCellFormat fileNameFormat = new WritableCellFormat(fileNameFont);
fileNameLabel.setCellFormat(fileNameFormat);
sheet.addCell(fileNameLabel);

// Create the link back to the index
link = new WritableHyperlink(0, 1, "Back to index", sheet, 0, currentSheet + NUMBER_OF_ROWS_IN_TITLE - 1);
workbook.getSheet(currentSheet).addHyperlink(link);
//Set column A of each sheet to be wide enough to show "Back to index"
workbook.getSheet(currentSheet).setColumnView(0, 13);
for (int currentSheet = 1; currentSheet < workbook.getNumberOfSheets(); currentSheet++) {
Sheet tableSheet = workbook.getSheetAt(currentSheet);
int rowIndex = currentSheet - 1 + NUMBER_OF_ROWS_IN_TITLE;

Row row = getOrCreateRow(sheet, rowIndex);
Cell linkCell = row.createCell(0);
linkCell.setCellValue(tableSheet.getSheetName());
linkCell.setHyperlink(workbook.getCreationHelper().createHyperlink(HyperlinkType.DOCUMENT));
linkCell.getHyperlink().setAddress("'" + tableSheet.getSheetName() + "'!A1");
linkCell.setCellStyle(consumerStyles.hyperlinkStyle);
String fileName = getCellString(tableSheet, 1, 1);
Cell fileNameCell = row.createCell(1);
fileNameCell.setCellValue(fileName);
fileNameCell.setCellStyle(indexFileNameStyle());

Row backLinkRow = getOrCreateRow(tableSheet, 1);
Cell backLinkCell = backLinkRow.createCell(0);
backLinkCell.setCellValue("Back to index");
backLinkCell.setHyperlink(workbook.getCreationHelper().createHyperlink(HyperlinkType.DOCUMENT));
backLinkCell.getHyperlink().setAddress("'" + sheet.getSheetName() + "'!A" + (rowIndex + 1));
backLinkCell.setCellStyle(consumerStyles.hyperlinkStyle);
tableSheet.setColumnWidth(0, 13 * 256);
}

// Make Column A fairly wide to show tab names and hide column B
sheet.setColumnView(0, 35);
sheet.setColumnView(1, 0);

sheet.setColumnWidth(0, 35 * 256);
sheet.setColumnWidth(1, 1);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private CellStyle indexFileNameStyle() {
return consumerStyles.indexFileNameStyle;
}

/**
* Converts camel capped names to something we can show in a spreadsheet.
*
* @param name Name to convert.
* @return A human readable version of the name wtih camel caps replaced by spaces.
*/
private String spreadsheetifyName(String name) {
return StringUtils.capitalize(name).replaceAll("([A-Z])", " $1").trim();
}


/**
* Inserts a row at the top of the sheet with the given title
* @param sheet add the title to
* @param title to add
*/
protected void createTitle(WritableSheet sheet, String title) {
protected void createTitle(Sheet sheet, String title) {
try {
Label cell = new Label(0, 0, title);
WritableFont headingFont = new WritableFont(WritableFont.ARIAL, 16, WritableFont.BOLD);
WritableCellFormat headingFormat = new WritableCellFormat(headingFont);
cell.setCellFormat(headingFormat);
sheet.addCell(cell);

cell = new Label(12, 0, "Copyright " + new SimpleDateFormat("yyyy").format(new Date()) + " Alfa Financial Software Ltd.");
WritableCellFormat copyrightFormat = new WritableCellFormat();
copyrightFormat.setAlignment(Alignment.RIGHT);
cell.setCellFormat(copyrightFormat);
sheet.addCell(cell);
Cell cell = getOrCreateRow(sheet, 0).createCell(0);
cell.setCellValue(title);
cell.setCellStyle(consumerStyles.headingStyle);

Cell copyrightCell = getOrCreateRow(sheet, 0).createCell(12);
copyrightCell.setCellValue("Copyright " + new SimpleDateFormat("yyyy").format(new Date()) + " Alfa Financial Software Ltd.");
copyrightCell.setCellStyle(consumerStyles.copyrightStyle);
} catch (Exception e) {
throw new RuntimeException(e);
}
}


/**
* @see org.alfasoftware.morf.dataset.DataSetConsumer#close(CloseState)
*/
@Override
public void close(CloseState closeState) {
try {

// Create the index
createIndex();

workbook.write();
workbook.write(documentOutputStream);
workbook.close();
} catch (Exception e) {
} catch (IOException e) {
throw new RuntimeException("Error closing writable workbook", e);
}
}


/**
* @see org.alfasoftware.morf.dataset.DataSetConsumer#table(org.alfasoftware.morf.metadata.Table, java.lang.Iterable)
*/
@Override
public void table(Table table, Iterable<Record> records) {
Integer maxSampleRows = DEFAULT_SAMPLE_ROWS;
if (rowsPerTable.isPresent()) {
maxSampleRows = rowsPerTable.get().get(table.getName().toUpperCase());
}

if (maxSampleRows == null) {
log.info("File [" + table.getName() + "] excluded in configuration." );
log.info("File [" + table.getName() + "] excluded in configuration.");
} else if (tableOutputter.tableHasUnsupportedColumns(table)) {
log.info("File [" + table.getName() + "] skipped - unsupported columns." );
log.info("File [" + table.getName() + "] skipped - unsupported columns.");
} else {
log.info("File [" + table.getName() + "] generating..." );
log.info("File [" + table.getName() + "] generating...");
tableOutputter.table(maxSampleRows, workbook, table, records);
}
}

private Row getOrCreateRow(Sheet sheet, int rowIndex) {
Row row = sheet.getRow(rowIndex);
return row == null ? sheet.createRow(rowIndex) : row;
}

private String getCellString(Sheet sheet, int columnIndex, int rowIndex) {
Row row = sheet.getRow(rowIndex);
if (row == null) {
return "";
}
Cell cell = row.getCell(columnIndex);
return cell == null ? "" : cell.toString();
}

private String safeSheetName(String name) {
return WorkbookUtil.createSafeSheetName(name);
}

private static final class ConsumerStyles {
private final CellStyle indexFileNameStyle;
private final CellStyle headingStyle;
private final CellStyle copyrightStyle;

private final CellStyle hyperlinkStyle;

private ConsumerStyles(Workbook workbook) {
Font normalFont = workbook.createFont();
normalFont.setColor(Font.COLOR_NORMAL);

Font headingFont = workbook.createFont();
headingFont.setBold(true);
headingFont.setFontHeightInPoints((short) 16);

Font hyperlinkFont = workbook.createFont();
hyperlinkFont.setUnderline(Font.U_SINGLE);
hyperlinkFont.setColor(IndexedColors.BLUE.getIndex());
hyperlinkFont.setFontHeightInPoints((short) 8);


indexFileNameStyle = workbook.createCellStyle();
indexFileNameStyle.setFont(normalFont);

headingStyle = workbook.createCellStyle();
headingStyle.setFont(headingFont);

copyrightStyle = workbook.createCellStyle();
copyrightStyle.setAlignment(HorizontalAlignment.RIGHT);

hyperlinkStyle = workbook.createCellStyle();
hyperlinkStyle.setFont(hyperlinkFont);
}
}
}
Loading