You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
456 lines
15 KiB
456 lines
15 KiB
/*******************************************************************************
|
|
* Copyright (C) 2017 Bstek.com
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
package com.bstek.ureport.build;
|
|
|
|
import java.sql.Connection;
|
|
import java.sql.SQLException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.logging.Logger;
|
|
|
|
import org.springframework.beans.BeansException;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationContextAware;
|
|
|
|
import com.bstek.ureport.Utils;
|
|
import com.bstek.ureport.build.cell.CellBuilder;
|
|
import com.bstek.ureport.build.cell.NoneExpandBuilder;
|
|
import com.bstek.ureport.build.cell.down.DownExpandBuilder;
|
|
import com.bstek.ureport.build.cell.right.RightExpandBuilder;
|
|
import com.bstek.ureport.build.paging.BasePagination;
|
|
import com.bstek.ureport.build.paging.Page;
|
|
import com.bstek.ureport.definition.Band;
|
|
import com.bstek.ureport.definition.Expand;
|
|
import com.bstek.ureport.definition.Orientation;
|
|
import com.bstek.ureport.definition.PagingMode;
|
|
import com.bstek.ureport.definition.Paper;
|
|
import com.bstek.ureport.definition.ReportDefinition;
|
|
import com.bstek.ureport.definition.datasource.BuildinDatasource;
|
|
import com.bstek.ureport.definition.datasource.BuildinDatasourceDefinition;
|
|
import com.bstek.ureport.definition.datasource.DatasourceDefinition;
|
|
import com.bstek.ureport.definition.datasource.DatasourceProvider;
|
|
import com.bstek.ureport.definition.datasource.JdbcDatasourceDefinition;
|
|
import com.bstek.ureport.definition.datasource.SpringBeanDatasourceDefinition;
|
|
import com.bstek.ureport.exception.ReportComputeException;
|
|
import com.bstek.ureport.exception.ReportException;
|
|
import com.bstek.ureport.model.Cell;
|
|
import com.bstek.ureport.model.Column;
|
|
import com.bstek.ureport.model.Report;
|
|
import com.bstek.ureport.model.Row;
|
|
|
|
/**
|
|
* @author Jacky.gao
|
|
* @since 2016年11月1日
|
|
*/
|
|
public class ReportBuilder extends BasePagination implements ApplicationContextAware{
|
|
public static final String BEAN_ID="ureport.reportBuilder";
|
|
private static final Logger log=Logger.getGlobal();
|
|
private ApplicationContext applicationContext;
|
|
private Map<String,DatasourceProvider> datasourceProviderMap=new HashMap<String,DatasourceProvider>();
|
|
private Map<Expand,CellBuilder> cellBuildersMap=new HashMap<Expand,CellBuilder>();
|
|
private NoneExpandBuilder noneExpandBuilder=new NoneExpandBuilder();
|
|
private HideRowColumnBuilder hideRowColumnBuilder;
|
|
public ReportBuilder() {
|
|
cellBuildersMap.put(Expand.Right,new RightExpandBuilder());
|
|
cellBuildersMap.put(Expand.Down,new DownExpandBuilder());
|
|
cellBuildersMap.put(Expand.None,noneExpandBuilder);
|
|
}
|
|
public Report buildReport(ReportDefinition reportDefinition,Map<String,Object> parameters) {
|
|
Report report = reportDefinition.newReport();
|
|
Map<String,Dataset> datasetMap=buildDatasets(reportDefinition, parameters, applicationContext);
|
|
Context context = new Context(this,report,datasetMap,applicationContext,parameters,hideRowColumnBuilder);
|
|
long start=System.currentTimeMillis();
|
|
List<Cell> cells=new ArrayList<Cell>();
|
|
cells.add(report.getRootCell());
|
|
do {
|
|
buildCell(context,cells);
|
|
cells = context.nextUnprocessedCells();
|
|
} while (cells != null);
|
|
doFillBlankRows(report,context);
|
|
recomputeCells(report,context);
|
|
long end=System.currentTimeMillis();
|
|
log.info("Report compute completed:"+(end-start)+"ms");
|
|
return report;
|
|
}
|
|
|
|
public void buildCell(Context context,List<Cell> cells){
|
|
if(cells==null){
|
|
cells=context.nextUnprocessedCells();
|
|
}
|
|
if(cells==null){
|
|
return;
|
|
}
|
|
for(Cell cell:cells){
|
|
List<BindData> dataList=context.buildCellData(cell);
|
|
cell.setProcessed(true);
|
|
int size=dataList.size();
|
|
Cell lastCell=cell;
|
|
if(size==1){
|
|
lastCell=noneExpandBuilder.buildCell(dataList, cell, context);
|
|
}else if(size>1){
|
|
CellBuilder cellBuilder=cellBuildersMap.get(cell.getExpand());
|
|
lastCell=cellBuilder.buildCell(dataList,cell, context);
|
|
}
|
|
if(lastCell.isFillBlankRows() && lastCell.getMultiple()>0){
|
|
int result=size % lastCell.getMultiple();
|
|
if(result>0){
|
|
int value=lastCell.getMultiple()-result;
|
|
context.addFillBlankRow(lastCell.getRow(), value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private Map<String,Dataset> buildDatasets(ReportDefinition reportDefinition,Map<String,Object> parameters,ApplicationContext applicationContext){
|
|
Map<String,Dataset> datasetMap=new HashMap<String,Dataset>();
|
|
List<DatasourceDefinition> datasources=reportDefinition.getDatasources();
|
|
if(datasources==null){
|
|
return datasetMap;
|
|
}
|
|
for(DatasourceDefinition dsDef:datasources){
|
|
if(dsDef instanceof JdbcDatasourceDefinition){
|
|
String dsName=dsDef.getName();
|
|
Connection conn=null;
|
|
try{
|
|
if(datasourceProviderMap.containsKey(dsName)){
|
|
conn=datasourceProviderMap.get(dsName).getConnection();
|
|
}
|
|
JdbcDatasourceDefinition ds=(JdbcDatasourceDefinition)dsDef;
|
|
List<Dataset> ls=ds.buildDatasets(conn, parameters);
|
|
if(ls!=null){
|
|
for(Dataset dataset:ls){
|
|
datasetMap.put(dataset.getName(), dataset);
|
|
}
|
|
}
|
|
}finally{
|
|
if(conn!=null){
|
|
try {
|
|
conn.close();
|
|
} catch (SQLException e) {}
|
|
}
|
|
}
|
|
}else if(dsDef instanceof SpringBeanDatasourceDefinition){
|
|
SpringBeanDatasourceDefinition ds=(SpringBeanDatasourceDefinition)dsDef;
|
|
List<Dataset> ls=ds.getDatasets(applicationContext, parameters);
|
|
if(ls!=null){
|
|
for(Dataset dataset:ls){
|
|
datasetMap.put(dataset.getName(), dataset);
|
|
}
|
|
}
|
|
}else if(dsDef instanceof BuildinDatasourceDefinition){
|
|
String dsName=dsDef.getName();
|
|
Connection conn=null;
|
|
try{
|
|
if(datasourceProviderMap.containsKey(dsName)){
|
|
conn=datasourceProviderMap.get(dsName).getConnection();
|
|
}
|
|
for(BuildinDatasource datasource:Utils.getBuildinDatasources()){
|
|
if(datasource.name().equals(dsName)){
|
|
conn=datasource.getConnection();
|
|
break;
|
|
}
|
|
}
|
|
if(conn==null){
|
|
throw new ReportComputeException("Buildin datasource ["+dsName+"] not exist.");
|
|
}
|
|
BuildinDatasourceDefinition ds=(BuildinDatasourceDefinition)dsDef;
|
|
List<Dataset> ls=ds.buildDatasets(conn, parameters);
|
|
if(ls!=null){
|
|
for(Dataset dataset:ls){
|
|
datasetMap.put(dataset.getName(), dataset);
|
|
}
|
|
}
|
|
}finally{
|
|
if(conn!=null){
|
|
try {
|
|
conn.close();
|
|
} catch (SQLException e) {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return datasetMap;
|
|
}
|
|
|
|
private void doFillBlankRows(Report report,Context context){
|
|
Map<Row, Integer> map=context.getFillBlankRowsMap();
|
|
List<Row> newRowList=new ArrayList<Row>();
|
|
for(Row row:map.keySet()){
|
|
int size=map.get(row);
|
|
Row lastRow=findLastRow(row, report);
|
|
for(int i=0;i<size;i++){
|
|
Row newRow=buildNewRow(lastRow, report);
|
|
newRowList.add(newRow);
|
|
}
|
|
int rowNumber=lastRow.getRowNumber();
|
|
if(newRowList.size()>0){
|
|
report.insertRows(rowNumber+1, newRowList);
|
|
newRowList.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
private Row buildNewRow(Row row,Report report){
|
|
Row newRow=row.newRow();
|
|
newRow.setBand(null);
|
|
List<Row> rows=report.getRows();
|
|
List<Column> columns=report.getColumns();
|
|
int start=-1,colSize=columns.size();
|
|
Map<Row, Map<Column, Cell>> rowMap=report.getRowColCellMap();
|
|
Map<Column,Cell> newCellMap=new HashMap<Column,Cell>();
|
|
rowMap.put(newRow, newCellMap);
|
|
|
|
Map<Column, Cell> colMap=rowMap.get(row);
|
|
for(int index=0;index<colSize;index++){
|
|
Column column=columns.get(index);
|
|
Cell currentCell=colMap.get(column);
|
|
if(currentCell==null){
|
|
if(start==-1){
|
|
start=row.getRowNumber()-2;
|
|
}
|
|
for(int i=start;i>-1;i--){
|
|
Row currentRow=rows.get(i);
|
|
Map<Column, Cell> prevColMap=rowMap.get(currentRow);
|
|
if(prevColMap.containsKey(column)){
|
|
currentCell=prevColMap.get(column);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(currentCell==null){
|
|
throw new ReportException("Insert blank rows fail.");
|
|
}
|
|
int colSpan=currentCell.getColSpan();
|
|
if(colSpan>0){
|
|
colSpan--;
|
|
index+=colSpan;
|
|
}
|
|
Cell newCell=newBlankCell(currentCell, column, report);
|
|
newCell.setRow(newRow);
|
|
newRow.getCells().add(newCell);
|
|
newCellMap.put(newCell.getColumn(), newCell);
|
|
}
|
|
return newRow;
|
|
}
|
|
|
|
private Row findLastRow(Row row,Report report){
|
|
List<Row> rows=report.getRows();
|
|
List<Cell> cells=row.getCells();
|
|
Row lastRow=row;
|
|
int span=0;
|
|
for(Cell cell:cells){
|
|
int rowSpan=cell.getRowSpan();
|
|
if(rowSpan<2){
|
|
continue;
|
|
}
|
|
if(span==0){
|
|
span=rowSpan;
|
|
}else if(rowSpan>span){
|
|
span=rowSpan;
|
|
}
|
|
}
|
|
if(span>1){
|
|
int rowIndex=row.getRowNumber()-1+span-1;
|
|
lastRow=rows.get(rowIndex);
|
|
}
|
|
return lastRow;
|
|
}
|
|
|
|
|
|
private Cell newBlankCell(Cell cell,Column column,Report report){
|
|
Cell newCell=new Cell();
|
|
newCell.setData("");
|
|
newCell.setColSpan(cell.getColSpan());
|
|
newCell.setConditionPropertyItems(cell.getConditionPropertyItems());
|
|
report.addLazyCell(newCell);
|
|
newCell.setCellStyle(cell.getCellStyle());
|
|
newCell.setName(cell.getName());
|
|
newCell.setColumn(column);
|
|
column.getCells().add(newCell);
|
|
Cell leftParent=cell.getLeftParentCell();
|
|
if(leftParent!=null){
|
|
newCell.setLeftParentCell(leftParent);
|
|
leftParent.addRowChild(newCell);
|
|
}
|
|
Cell topParent=cell.getTopParentCell();
|
|
if(topParent!=null){
|
|
newCell.setTopParentCell(topParent);
|
|
topParent.addColumnChild(newCell);
|
|
}
|
|
return newCell;
|
|
}
|
|
|
|
private void recomputeCells(Report report,Context context){
|
|
List<Cell> lazyCells=report.getLazyComputeCells();
|
|
for(Cell cell:lazyCells){
|
|
cell.doCompute(context);
|
|
}
|
|
context.setDoPaging(true);
|
|
List<Row> rows=report.getRows();
|
|
int rowSize=rows.size();
|
|
Paper paper = report.getPaper();
|
|
PagingMode pagingMode=paper.getPagingMode();
|
|
List<Row> headerRows=report.getHeaderRepeatRows();
|
|
List<Row> footerRows=report.getFooterRepeatRows();
|
|
List<Row> titleRows=report.getTitleRows();
|
|
List<Row> summaryRows=report.getSummaryRows();
|
|
List<Page> pages=new ArrayList<Page>();
|
|
List<Row> pageRows=new ArrayList<Row>();
|
|
int pageIndex=1;
|
|
List<Row> pageRepeatHeaders=new ArrayList<Row>();
|
|
List<Row> pageRepeatFooters=new ArrayList<Row>();
|
|
pageRepeatHeaders.addAll(headerRows);
|
|
pageRepeatFooters.addAll(footerRows);
|
|
if(pagingMode.equals(PagingMode.fitpage)){
|
|
int height=paper.getHeight()-paper.getBottomMargin()-paper.getTopMargin()-5;
|
|
if(paper.getOrientation().equals(Orientation.landscape)){
|
|
height=paper.getWidth()-paper.getBottomMargin()-paper.getTopMargin()-5;
|
|
}
|
|
int repeatHeaderRowHeight=report.getRepeatHeaderRowHeight(),repeatFooterRowHeight=report.getRepeatFooterRowHeight();
|
|
int titleRowHeight=report.getTitleRowsHeight();
|
|
int rowHeight=titleRowHeight+repeatHeaderRowHeight+repeatFooterRowHeight;
|
|
for(int i=0;i<rowSize;i++){
|
|
Row row=rows.get(i);
|
|
int rowRealHeight=row.getRealHeight();
|
|
if(rowRealHeight==0){
|
|
continue;
|
|
}
|
|
Band band=row.getBand();
|
|
if(band!=null){
|
|
String rowKey=row.getRowKey();
|
|
int index=-1;
|
|
if(band.equals(Band.headerrepeat)){
|
|
for(int j=0;j<pageRepeatHeaders.size();j++){
|
|
Row headerRow=pageRepeatHeaders.get(j);
|
|
if(headerRow.getRowKey().equals(rowKey)){
|
|
index=j;
|
|
break;
|
|
}
|
|
}
|
|
pageRepeatHeaders.remove(index);
|
|
pageRepeatHeaders.add(index,row);
|
|
}else if(band.equals(Band.footerrepeat)){
|
|
for(int j=0;j<pageRepeatFooters.size();j++){
|
|
Row footerRow=pageRepeatFooters.get(j);
|
|
if(footerRow.getRowKey().equals(rowKey)){
|
|
index=j;
|
|
break;
|
|
}
|
|
}
|
|
pageRepeatFooters.remove(index);
|
|
pageRepeatFooters.add(index,row);
|
|
}
|
|
continue;
|
|
}
|
|
rowHeight+=rowRealHeight+1;
|
|
pageRows.add(row);
|
|
boolean overflow=false;
|
|
if((i+1)<rows.size()){
|
|
Row nextRow=rows.get(i+1);
|
|
if((rowHeight+nextRow.getRealHeight()) > height){
|
|
overflow=true;
|
|
}
|
|
}
|
|
if(!overflow && row.isPageBreak()){
|
|
overflow=true;
|
|
}
|
|
if(overflow){
|
|
Page newPage=buildPage(pageRows,pageRepeatHeaders,pageRepeatFooters,titleRows,pageIndex,report);
|
|
pageIndex++;
|
|
pages.add(newPage);
|
|
rowHeight=repeatHeaderRowHeight+repeatFooterRowHeight;
|
|
pageRows=new ArrayList<Row>();
|
|
}
|
|
}
|
|
if(pageRows.size()>0){
|
|
Page newPage=buildPage(pageRows,pageRepeatHeaders,pageRepeatFooters,titleRows,pageIndex,report);
|
|
pages.add(newPage);
|
|
}
|
|
buildPageHeaderFooter(pages, report);
|
|
}else{
|
|
int fixRows=paper.getFixRows()-headerRows.size()-footerRows.size();
|
|
if(fixRows<0){
|
|
fixRows=1;
|
|
}
|
|
for(int i=0;i<rowSize;i++){
|
|
Row row=rows.get(i);
|
|
int height=row.getRealHeight();
|
|
if(height==0){
|
|
continue;
|
|
}
|
|
Band band=row.getBand();
|
|
if(band!=null){
|
|
String rowKey=row.getRowKey();
|
|
int index=-1;
|
|
if(band.equals(Band.headerrepeat)){
|
|
for(int j=0;j<pageRepeatHeaders.size();j++){
|
|
Row headerRow=pageRepeatHeaders.get(j);
|
|
if(headerRow.getRowKey().equals(rowKey)){
|
|
index=j;
|
|
break;
|
|
}
|
|
}
|
|
pageRepeatHeaders.remove(index);
|
|
pageRepeatHeaders.add(index,row);
|
|
}else if(band.equals(Band.footerrepeat)){
|
|
for(int j=0;j<pageRepeatFooters.size();j++){
|
|
Row footerRow=pageRepeatFooters.get(j);
|
|
if(footerRow.getRowKey().equals(rowKey)){
|
|
index=j;
|
|
break;
|
|
}
|
|
}
|
|
pageRepeatFooters.remove(index);
|
|
pageRepeatFooters.add(index,row);
|
|
}
|
|
continue;
|
|
}
|
|
pageRows.add(row);
|
|
if((pageRows.size()+footerRows.size()) >= fixRows){
|
|
Page newPage=buildPage(pageRows,pageRepeatHeaders,pageRepeatFooters,titleRows,pageIndex,report);
|
|
pageIndex++;
|
|
pages.add(newPage);
|
|
pageRows=new ArrayList<Row>();
|
|
}
|
|
}
|
|
if(pageRows.size()>0){
|
|
Page newPage=buildPage(pageRows,pageRepeatHeaders,pageRepeatFooters,titleRows,pageIndex,report);
|
|
pages.add(newPage);
|
|
}
|
|
buildPageHeaderFooter(pages, report);
|
|
}
|
|
buildSummaryRows(summaryRows, pages);
|
|
report.setPages(pages);
|
|
}
|
|
|
|
public void setHideRowColumnBuilder(HideRowColumnBuilder hideRowColumnBuilder) {
|
|
this.hideRowColumnBuilder = hideRowColumnBuilder;
|
|
}
|
|
|
|
@Override
|
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
|
this.applicationContext=applicationContext;
|
|
Collection<DatasourceProvider> datasourceProviders=applicationContext.getBeansOfType(DatasourceProvider.class).values();
|
|
for(DatasourceProvider dp: datasourceProviders){
|
|
datasourceProviderMap.put(dp.getName(), dp);
|
|
}
|
|
new Splash().doPrint();
|
|
}
|
|
}
|
|
|