/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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 Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.output.table.base;
import org.pentaho.reporting.engine.classic.core.layout.model.BlockRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.process.IterateSimpleStructureProcessStep;
public class TableLayoutProducer extends IterateSimpleStructureProcessStep {
private SheetLayout layout;
private long pageOffset;
private boolean headerProcessed;
private long contentOffset;
private long effectiveHeaderSize;
private long pageEndPosition;
private boolean unalignedPagebands;
private boolean processWatermark;
public TableLayoutProducer( final OutputProcessorMetaData metaData ) {
initialize( metaData );
this.layout = new SheetLayout( metaData );
}
private void initialize( final OutputProcessorMetaData metaData ) {
if ( metaData == null ) {
throw new NullPointerException();
}
this.processWatermark = metaData.isFeatureSupported( OutputProcessorFeature.WATERMARK_SECTION );
this.unalignedPagebands = metaData.isFeatureSupported( OutputProcessorFeature.UNALIGNED_PAGEBANDS );
}
public TableLayoutProducer( final OutputProcessorMetaData metaData, final SheetLayout sheetLayout ) {
initialize( metaData );
this.layout = sheetLayout;
}
public boolean isProcessWatermark() {
return processWatermark;
}
public void setProcessWatermark( final boolean processWatermark ) {
this.processWatermark = processWatermark;
}
public SheetLayout getLayout() {
return layout;
}
public void update( final LogicalPageBox logicalPage, final boolean iterativeUpdate ) {
if ( unalignedPagebands == false ) {
// The page-header and footer area are aligned/shifted within the logical pagebox so that all areas
// share a common coordinate system. This also implies, that the whole logical page is aligned content.
pageOffset = 0;
effectiveHeaderSize = 0;
pageEndPosition = logicalPage.getPageEnd();
// Log.debug ("Content Processing " + pageOffset + " -> " + pageEnd);
if ( startBox( logicalPage ) ) {
if ( headerProcessed == false ) {
if ( processWatermark ) {
startProcessing( logicalPage.getWatermarkArea() );
}
final BlockRenderBox headerArea = logicalPage.getHeaderArea();
startProcessing( headerArea );
headerProcessed = true;
}
processBoxChilds( logicalPage );
if ( iterativeUpdate == false ) {
final BlockRenderBox repeatFooterBox = logicalPage.getRepeatFooterArea();
startProcessing( repeatFooterBox );
final BlockRenderBox pageFooterBox = logicalPage.getFooterArea();
startProcessing( pageFooterBox );
}
}
finishBox( logicalPage );
} else {
// The page-header and footer area are not aligned/shifted within the logical pagebox.
// All areas have their own coordinate system starting at (0,0). We apply a manual shift here
// so that we dont have to modify the nodes (which invalidates the cache, and therefore is ugly)
effectiveHeaderSize = 0;
pageOffset = logicalPage.getPageOffset();
pageEndPosition = ( logicalPage.getPageEnd() );
if ( startBox( logicalPage ) ) {
if ( headerProcessed == false ) {
pageOffset = 0;
contentOffset = 0;
effectiveHeaderSize = 0;
if ( processWatermark ) {
final BlockRenderBox watermarkArea = logicalPage.getWatermarkArea();
pageEndPosition = watermarkArea.getHeight();
startProcessing( watermarkArea );
}
final BlockRenderBox headerArea = logicalPage.getHeaderArea();
pageEndPosition = headerArea.getHeight();
startProcessing( headerArea );
contentOffset = headerArea.getHeight();
headerProcessed = true;
}
pageOffset = logicalPage.getPageOffset();
pageEndPosition = logicalPage.getPageEnd();
effectiveHeaderSize = contentOffset;
processBoxChilds( logicalPage );
if ( iterativeUpdate == false ) {
pageOffset = 0;
final BlockRenderBox repeatFooterArea = logicalPage.getRepeatFooterArea();
final long repeatFooterOffset = contentOffset + ( logicalPage.getPageEnd() - logicalPage.getPageOffset() );
final long repeatFooterPageEnd = repeatFooterOffset + repeatFooterArea.getHeight();
effectiveHeaderSize = repeatFooterOffset;
pageEndPosition = repeatFooterPageEnd;
startProcessing( repeatFooterArea );
final BlockRenderBox footerArea = logicalPage.getFooterArea();
final long footerPageEnd = repeatFooterPageEnd + footerArea.getHeight();
effectiveHeaderSize = repeatFooterPageEnd;
pageEndPosition = footerPageEnd;
startProcessing( footerArea );
}
}
finishBox( logicalPage );
}
// try to remove as many nodes as you can ..
logicalPage.setProcessedTableOffset( logicalPage.getPageEnd() );
}
protected boolean startBox( final RenderBox box ) {
if ( box.isVisible() == false ) {
return false;
}
if ( box.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT ) {
processRenderableContent( (RenderableReplacedContentBox) box );
return false;
}
return startBoxInternal( box );
}
private boolean startBoxInternal( final RenderBox box ) {
final long height = box.getHeight();
//
// DebugLog.log ("Processing Box " + pageOffset + " " + effectiveHeaderSize + " " + box.getY() + " " + height);
// DebugLog.log ("Processing Box " + box);
if ( height > 0 ) {
if ( ( box.getY() + height ) <= pageOffset ) {
return false;
}
if ( box.getY() >= pageEndPosition ) {
return false;
}
} else {
// zero height boxes are always a bit tricky ..
if ( ( box.getY() + height ) < pageOffset ) {
return false;
}
if ( box.getY() > pageEndPosition ) {
return false;
}
}
if ( box.isOpen() == false && box.isFinishedTable() == false && box.isCommited() ) {
if ( layout.add( box, pageOffset, effectiveHeaderSize, pageEndPosition ) ) {
return false;
}
box.setFinishedTable( true );
return true;
}
return true;
}
protected void processRenderableContent( final RenderableReplacedContentBox box ) {
if ( box.isOpen() == false && box.isFinishedTable() == false && box.isCommited() ) {
startBoxInternal( box );
layout.addRenderableContent( box, pageOffset, effectiveHeaderSize, pageEndPosition );
}
}
protected void processBoxChilds( final RenderBox box ) {
if ( box.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH ) {
// not needed. Keep this method empty so that the paragraph childs are *not* processed.
return;
}
super.processBoxChilds( box );
}
public void pageCompleted() {
layout.pageCompleted();
headerProcessed = false;
}
/**
* A designtime support method to compute a sheet layout for the given section. A new sheetlayout is created on each
* call.
*
* @param box
* the section that should be processed.
* @return the computed sheet layout.
*/
public void computeDesigntimeConflicts( final RenderBox box ) {
clear();
pageEndPosition = box.getHeight();
startProcessing( box );
}
public void clear() {
this.layout.clear();
effectiveHeaderSize = 0;
pageOffset = 0;
pageEndPosition = 0;
contentOffset = 0;
headerProcessed = false;
}
}