/*
* 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 - 2009 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.CanvasRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.InlineRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphRenderBox;
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.IterateStructuralProcessStep;
/**
* Creation-Date: 02.05.2007, 18:31:37
*
* @author Thomas Morgner
*/
public class TableLayoutProducer extends IterateStructuralProcessStep
{
private SheetLayout layout;
private long pageOffset;
private boolean headerProcessed;
private long contentOffset;
private long effectiveOffset;
private boolean unalignedPagebands;
private long pageHeight;
private OutputProcessorMetaData metaData;
public TableLayoutProducer(final OutputProcessorMetaData metaData)
{
if (metaData == null)
{
throw new NullPointerException();
}
this.metaData = metaData;
this.unalignedPagebands = metaData.isFeatureSupported(OutputProcessorFeature.UNALIGNED_PAGEBANDS);
this.layout = new SheetLayout
(metaData.isFeatureSupported(AbstractTableOutputProcessor.STRICT_LAYOUT),
metaData.isFeatureSupported(AbstractTableOutputProcessor.TREAT_ELLIPSE_AS_RECTANGLE));
}
public SheetLayout getLayout()
{
return layout;
}
public void update(final LogicalPageBox logicalPage,
final boolean iterativeUpdate)
{
// this.iterativeUpdate = iterativeUpdate;
final BlockRenderBox pageFooterBox = logicalPage.getFooterArea();
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;
final long pageEnd = logicalPage.getPageEnd() - logicalPage.getPageOffset();
effectiveOffset = 0;
pageHeight = effectiveOffset + (pageEnd - pageOffset);
//Log.debug ("Content Processing " + pageOffset + " -> " + pageEnd);
if (startBlockBox(logicalPage))
{
if (headerProcessed == false)
{
startProcessing(logicalPage.getWatermarkArea());
final BlockRenderBox headerArea = logicalPage.getHeaderArea();
startProcessing(headerArea);
headerProcessed = true;
}
processBoxChilds(logicalPage);
if (iterativeUpdate == false)
{
pageHeight += pageFooterBox.getHeight();
startProcessing(pageFooterBox);
}
}
finishBlockBox(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)
effectiveOffset = 0;
pageOffset = 0;
pageHeight = (logicalPage.getPageEnd());
if (startBlockBox(logicalPage))
{
if (headerProcessed == false)
{
contentOffset = 0;
effectiveOffset = 0;
final BlockRenderBox watermarkArea = logicalPage.getWatermarkArea();
final long watermarkPageEnd = watermarkArea.getHeight();
pageHeight = effectiveOffset + (watermarkPageEnd - pageOffset);
startProcessing(watermarkArea);
final BlockRenderBox headerArea = logicalPage.getHeaderArea();
final long pageEnd = headerArea.getHeight();
pageHeight = effectiveOffset + (pageEnd - pageOffset);
startProcessing(headerArea);
contentOffset = headerArea.getHeight();
headerProcessed = true;
}
pageOffset = logicalPage.getPageOffset();
final long pageEnd = logicalPage.getPageEnd();
effectiveOffset = contentOffset;
pageHeight = effectiveOffset + (pageEnd - pageOffset);
processBoxChilds(logicalPage);
if (iterativeUpdate == false)
{
pageOffset = 0;
final BlockRenderBox footerArea = pageFooterBox;
final long footerOffset = contentOffset + (logicalPage.getPageEnd() - logicalPage.getPageOffset());
final long footerPageEnd = footerOffset + footerArea.getHeight();
effectiveOffset = footerOffset;
pageHeight = effectiveOffset + (footerPageEnd - pageOffset);
startProcessing(footerArea);
}
}
finishBlockBox(logicalPage);
}
}
/**
* A designtime support method to compute a sheet layout for the given section. A new sheetlayout is created
* on each call.
*
* @param section the section that should be processed.
* @return the computed sheet layout.
*/
public SheetLayout createSheetLayout(final RenderBox section)
{
this.layout = new SheetLayout
(metaData.isFeatureSupported(AbstractTableOutputProcessor.STRICT_LAYOUT),
metaData.isFeatureSupported(AbstractTableOutputProcessor.TREAT_ELLIPSE_AS_RECTANGLE));
pageOffset = 0;
effectiveOffset = 0;
contentOffset = 0;
pageHeight = section.getHeight();
startProcessing(section);
return layout;
}
private boolean startBox(final RenderBox box)
{
final long y = effectiveOffset + box.getY() - pageOffset;
final long height = box.getHeight();
//
// DebugLog.log ("Processing Box " + effectiveOffset + " " + y + " " + height);
// DebugLog.log ("Processing Box " + box);
if (height > 0)
{
if ((y + height) <= effectiveOffset)
{
return false;
}
if (y >= pageHeight)
{
return false;
}
}
else
{
// zero height boxes are always a bit tricky ..
if ((y + height) < effectiveOffset)
{
return false;
}
if (y > pageHeight)
{
return false;
}
}
// This changes the rendering order; the computed background are most likely not 100% correct.
// todo: Insert the box multiple times and replace the background on the whole area where not already merged.
// todo: If open, do not produce a bottom-border.
// OK, for now, we dont have to worry, as non-rootlevel boxes have neither border or backgrounds. But before
// we push this code into 0.9 we should add some handling here ..
// if (box.isOpen())
// {
// layout.add(box, -pageOffset, true);
// }
if (box.isOpen() == false &&
box.isFinished() == false &&
box.isCommited())
{
if (layout.add(box, -pageOffset + effectiveOffset))
{
return false;
}
box.setFinished(true);
return true;
}
return true;
}
protected boolean startBlockBox(final BlockRenderBox box)
{
return startBox(box);
}
protected boolean startInlineBox(final InlineRenderBox box)
{
// we should not have come that far ..
return false;
}
protected boolean startOtherBox(final RenderBox box)
{
return startBox(box);
}
public boolean startCanvasBox(final CanvasRenderBox box)
{
return startBox(box);
}
protected boolean startRowBox(final RenderBox box)
{
return startBox(box);
}
protected void processRenderableContent(final RenderableReplacedContentBox box)
{
if (box.isOpen() == false &&
box.isFinished() == false &&
box.isCommited())
{
startBox(box);
layout.addRenderableContent(box, -pageOffset + effectiveOffset);
}
}
protected void processParagraphChilds(final ParagraphRenderBox box)
{
// not needed. Keep this method empty so that the paragraph childs are *not* processed.
}
public void pageCompleted()
{
layout.pageCompleted();
headerProcessed = false;
}
}