/*
* 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;
}
}