package com.ejie.x38.reports;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.servlet.view.AbstractView;
import com.ejie.x38.dto.JerarquiaDto;
/* https://jira.springsource.org/browse/SPR-6898 */
public abstract class AbstractPOIExcelView extends AbstractView {
/** The content type for an Excel response */
private static final String CONTENT_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
private static final String CONTENT_TYPE_XLS = "application/vnd.ms-excel";
/**
* Default Constructor. Sets the content type of the view for excel files.
*/
public AbstractPOIExcelView() {
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
/**
* Renders the Excel view, given the specified model.
*/
@Override
protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Workbook workbook = createWorkbook();
if (workbook instanceof HSSFWorkbook) {
setContentType(CONTENT_TYPE_XLS);
} else {
setContentType(CONTENT_TYPE_XLSX);
}
buildExcelDocument(model, workbook, request, response);
// Set the content type.
response.setContentType(getContentType());
// Flush byte array to servlet output stream.
ServletOutputStream out = response.getOutputStream();
out.flush();
workbook.write(out);
out.flush();
// Don't close the stream - we didn't open it, so let the container
// handle it.
// http://stackoverflow.com/questions/1829784/should-i-close-the-servlet-outputstream
}
/**
* Subclasses must implement this method to create an Excel Workbook.
* HSSFWorkbook and XSSFWorkbook are both possible formats.
*/
protected abstract Workbook createWorkbook();
/**
* Subclasses must implement this method to create an Excel Workbook.
* HSSFWorkbook and XSSFWorkbook are both possible formats.
*/
protected abstract String getFileExtension();
/**
* Subclasses must implement this method to create an Excel HSSFWorkbook
* document, given the model.
*
* @param model
* the model Map
* @param workbook
* the Excel workbook to complete
* @param request
* in case we need locale etc. Shouldn't look at attributes.
* @param response
* in case we need to set cookies. Shouldn't write to it.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void buildExcelDocument(Map<String, Object> model, Workbook workbook,
HttpServletRequest request, HttpServletResponse response) throws Exception {
//Nombre Fichero
response.setHeader("Content-Disposition", "attachment; filename=\"" + model.get("fileName") + getFileExtension() + "\"");
//Procesar Hojas
List<ReportData> reportData = (List<ReportData>) model.get("reportData");
for (ReportData dataSheet : reportData) {
//Metadatos
boolean isJerarquia = dataSheet.isJerarquia();
boolean isGrouping = dataSheet.isGrouping();
JerarquiaMetadata jmd = dataSheet.getJerarquiaMetadada();
//Hoja
Sheet sheet = workbook.createSheet(dataSheet.getSheetName());
//Cabeceras Hoja
Row row = sheet.createRow(0);
int cellNum = 0;
int rowNum = 0;
LinkedHashMap<String, String> dataHeader = (LinkedHashMap<String, String>) dataSheet.getHeaderNames();
if (dataSheet.isShowHeaders()){
//Columna para la agrupacion
if (isGrouping){
row.createCell(cellNum++).setCellValue("");
}
//Columna para marcar los elementos que cumplen filtro en Jerarquia
if (isJerarquia && jmd.isShowFiltered()){
if (!"".equals(jmd.getFilterHeaderName())){
row.createCell(cellNum++).setCellValue(jmd.getFilterHeaderName());
} else {
row.createCell(cellNum++).setCellValue("");
}
}
for (Map.Entry<String, String> entry : dataHeader.entrySet()) {
//Si tiene agrupacion, no debe mostrarse la columna del grupo y es la columna del grupo comprobar => pasar al siguiente
if (isGrouping && !dataSheet.isShowGroupColumng() && entry.getKey().equals(dataSheet.getGroupColumnName())){
continue;
}
row.createCell(cellNum++).setCellValue(entry.getValue());
}
rowNum++;
}
//Datos Hoja
List<Object> modelData;
if (!isJerarquia){
modelData = dataSheet.getModelData();
} else {
modelData = dataSheet.getModelDataJerarquia();
}
String prevGroupValue="", groupValue = "";
for (Object object : modelData) {
//Gestión filas-columnas
row = sheet.createRow(rowNum++);
cellNum = 0;
//jerarquia
int level = 0;
boolean hasChildren = false;
//Si tiene agrupación => comprobar el cambio de grupo
if (isGrouping){
groupValue = BeanUtils.getProperty(((JerarquiaDto)object).getModel(), dataSheet.getGroupColumnName());
if (!groupValue.equals(prevGroupValue)){
prevGroupValue = groupValue;
row.createCell(cellNum++).setCellValue(groupValue);
row = sheet.createRow(rowNum++);
cellNum = 0;
}
row.createCell(cellNum++).setCellValue(""); //Espacio para evitar columna agrupación
}
//Si es jerarquia => comprobar si debe mostrar filtro y obtener atributo + parsear objeto de iteracion
if (isJerarquia){
if (jmd.isShowFiltered()){//Mostrar si cumple filtro
if (((JerarquiaDto) object).isFilter()){ //Cumple filtro
row.createCell(cellNum++).setCellValue(jmd.getFilterToken());
} else {
row.createCell(cellNum++).setCellValue("");
}
}
level = ((JerarquiaDto) object).getLevel();
hasChildren = ((JerarquiaDto) object).isHasChildren();
object = ((JerarquiaDto)object).getModel(); //Obtener objeto de negocio
}
for (Map.Entry<String, String> entry : dataHeader.entrySet()) {
//Si tiene agrupacion, no debe mostrarse la columna del grupo y es la columna del grupo comprobar => pasar al siguiente
if (isGrouping && !dataSheet.isShowGroupColumng() && entry.getKey().equals(dataSheet.getGroupColumnName())){
continue;
}
String columnValue = BeanUtils.getProperty(object, entry.getKey());
//Si es Jerarquia...
if (isJerarquia){
//Si está activados los iconos y la columna es la indicada => aplicar iconos
if (jmd.isShowIcon() && entry.getKey().equals(jmd.getIconColumnName())){
if (jmd.getIconCollapsedList().contains(BeanUtils.getProperty(object, jmd.getIconBeanAtribute()))){
columnValue = jmd.getIconUnexpanded() + columnValue;
} else {
if (hasChildren){
columnValue = jmd.getIconExpanded() + columnValue;
} else {
columnValue = jmd.getIconNoChild() + columnValue;
}
}
}
//Si está activada la tabulación y la columna es la indicada => aplicar tabulación
if (jmd.isShowTabbed() && entry.getKey().equals(jmd.getTabColumnName())){
columnValue = jmd.getTab(level) + columnValue;
}
}
row.createCell(cellNum++).setCellValue(columnValue);
}
}
//Autoajustar tamaño columnas
int columnNumber = dataHeader.size();
for (int i = 0; i < columnNumber; i++) {
sheet.autoSizeColumn(i);
}
}
//Gestión de cookie (determina el final de la generación del fichero)
Cookie cookie = new Cookie("fileDownload", "true");
cookie.setPath("/");
response.addCookie(cookie);
}
}