package org.jboss.seam.excel.exporter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.core.Interpolator;
import org.jboss.seam.core.Manager;
import org.jboss.seam.document.ByteArrayDocumentData;
import org.jboss.seam.document.DocumentData;
import org.jboss.seam.document.DocumentStore;
import org.jboss.seam.excel.ExcelFactory;
import org.jboss.seam.excel.ExcelWorkbook;
import org.jboss.seam.excel.ExcelWorkbookException;
import org.jboss.seam.excel.css.CSSNames;
import org.jboss.seam.excel.css.ColumnStyle;
import org.jboss.seam.excel.css.CSSParser;
import org.jboss.seam.excel.css.StyleMap;
import org.jboss.seam.excel.ui.ExcelComponent;
import org.jboss.seam.excel.ui.UICell;
import org.jboss.seam.excel.ui.UIColumn;
import org.jboss.seam.excel.ui.UIWorkbook;
import org.jboss.seam.excel.ui.UIWorksheet;
import org.jboss.seam.navigation.Pages;
/**
* Excel export class that exports a UIData component to an Excel workbook
*
* @author Nicklas Karlsson (nickarls@gmail.com)
* @author Daniel Roth (danielc.roth@gmail.com)
*
*/
@Name("org.jboss.seam.excel.exporter.excelExporter")
@Scope(ScopeType.EVENT)
@Install(precedence = Install.BUILT_IN)
@BypassInterceptors
public class ExcelExporter
{
// The excel workbook implementation
private ExcelWorkbook excelWorkbook = null;
// A map of known column widths
private Map<Integer, Integer> columnWidths = new HashMap<Integer, Integer>();
/**
* Helper method to call the exporter and use the default excel workbook
* implementation
*
* @param dataTableId
*/
public void export(String dataTableId)
{
export(dataTableId, "");
}
/**
* Exports the UIData object to Excel workbook. Looks up the component, parse
* the templates, iterates the columns and the UIOutput elements within
*
* @param dataTableId id of data table to export
* @param type ExcelWorkbook implementation to use
*/
@SuppressWarnings("unchecked")
public void export(String dataTableId, String type)
{
excelWorkbook = ExcelFactory.instance().getExcelWorkbook(type);
CSSParser parser = new CSSParser();
// Gets the datatable
UIData dataTable = (UIData) FacesContext.getCurrentInstance().getViewRoot().findComponent(dataTableId);
if (dataTable == null)
{
throw new ExcelWorkbookException(Interpolator.instance().interpolate("Could not find data table with id #0", dataTableId));
}
// Inits the workbook and worksheet
UIWorkbook uiWorkbook = new UIWorkbook();
excelWorkbook.createWorkbook(uiWorkbook);
UIWorksheet uiWorksheet = new UIWorksheet();
uiWorkbook.getChildren().add(uiWorksheet);
uiWorksheet.setStyle(CSSParser.getStyle(dataTable));
uiWorksheet.setStyleClass(CSSParser.getStyleClass(dataTable));
excelWorkbook.createOrSelectWorksheet(uiWorksheet);
// Saves the datatable var
String dataTableVar = dataTable.getVar();
Object oldValue = FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get(dataTableVar);
// Processes the columns
List<javax.faces.component.UIColumn> columns = ExcelComponent.getChildrenOfType(dataTable.getChildren(), javax.faces.component.UIColumn.class);
columnWidths = parseColumnWidths(uiWorksheet);
int col = 0;
for (javax.faces.component.UIColumn column : columns)
{
ColumnStyle columnStyle = new ColumnStyle(parser.getCascadedStyleMap(column));
boolean cssExport = columnStyle.export == null || columnStyle.export;
if (column.isRendered() && cssExport)
{
uiWorksheet.getChildren().add(column);
Iterator iterator = UIWorksheet.unwrapIterator(dataTable.getValue());
processColumn(column, iterator, dataTableVar, col++);
excelWorkbook.nextColumn();
}
}
// Restores the data table var
if (oldValue == null)
{
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().remove(dataTableVar);
}
else
{
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put(dataTableVar, oldValue);
}
// Redirects to the generated document
redirectExport();
}
/**
* Parses column widths from a worksheet tag
*
* @param worksheet The worksheet to get the style from
* @return The map of column number -> column width
*/
private Map<Integer, Integer> parseColumnWidths(UIWorksheet worksheet)
{
Map<Integer, Integer> columnWidths = new HashMap<Integer, Integer>();
CSSParser parser = new CSSParser();
StyleMap styleMap = parser.getCascadedStyleMap(worksheet);
for (Map.Entry<String, Object> entry : styleMap.entrySet())
{
String key = entry.getKey();
if (key.startsWith(CSSNames.COLUMN_WIDTHS))
{
String columnIndexString = key.substring(CSSNames.COLUMN_WIDTHS.length());
int columnIndex = Integer.parseInt(columnIndexString);
columnWidths.put(columnIndex, (Integer) entry.getValue());
}
}
return columnWidths;
}
/**
* Puts document in store and redirects
*/
private void redirectExport()
{
String viewId = Pages.getViewId(FacesContext.getCurrentInstance());
String baseName = Pages.getCurrentBaseName();
DocumentData documentData = new ByteArrayDocumentData(baseName, excelWorkbook.getDocumentType(), excelWorkbook.getBytes());
String id = DocumentStore.instance().newId();
String url = DocumentStore.instance().preferredUrlForContent(baseName, excelWorkbook.getDocumentType().getExtension(), id);
url = Manager.instance().encodeConversationId(url, viewId);
DocumentStore.instance().saveData(id, documentData);
try
{
FacesContext.getCurrentInstance().getExternalContext().redirect(url);
}
catch (IOException e)
{
throw new ExcelWorkbookException(Interpolator.instance().interpolate("Could not redirect to #0", url), e);
}
}
/**
* Processes a datatable column
*
* @param column The column to parse
* @param iterator The iterator to the data
* @param var The binding var
* @param col
*/
@SuppressWarnings("unchecked")
private void processColumn(javax.faces.component.UIColumn column, Iterator iterator, String var, int columnIndex)
{
// Process header facet
UIComponent headerFacet = column.getFacet(UIColumn.HEADER_FACET_NAME);
if (headerFacet != null && UIOutput.class.isAssignableFrom(headerFacet.getClass()))
{
List<UIOutput> headerOutputs = new ArrayList<UIOutput>();
headerOutputs.add((UIOutput) headerFacet);
processOutputs(column, headerOutputs);
}
// Process data
while (iterator.hasNext())
{
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put(var, iterator.next());
List<UIOutput> dataOutputs = ExcelComponent.getChildrenOfType(column.getChildren(), UIOutput.class);
processOutputs(column, dataOutputs);
}
Integer columnWidth = columnWidths.get(columnIndex);
if (columnWidth != null)
{
UIColumn uiColumn = new UIColumn();
uiColumn.setStyle(CSSNames.COLUMN_WIDTH + ":" + columnWidth);
excelWorkbook.applyColumnSettings(uiColumn);
}
}
/**
* Processes all output type elements (in column)
*
* @param outputs The list of outputs to process
* @param preTemplates The pre-pushed templates
*/
private void processOutputs(javax.faces.component.UIColumn column, List<UIOutput> outputs)
{
for (UIOutput output : outputs)
{
if (!output.isRendered())
{
continue;
}
UICell cell = new UICell();
column.getChildren().add(cell);
cell.setId(output.getId());
cell.setValue(output.getValue());
cell.setStyle(CSSParser.getStyle(output));
cell.setStyleClass(CSSParser.getStyleClass(output));
excelWorkbook.addItem(cell);
}
}
}