/*!
* 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) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.layout.process;
import org.pentaho.reporting.engine.classic.core.layout.model.FinishedRenderNode;
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.ParagraphRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.context.StaticBoxLayoutProperties;
import org.pentaho.reporting.engine.classic.core.layout.process.util.OrphanContext;
import org.pentaho.reporting.engine.classic.core.layout.process.util.OrphanContextPool;
import org.pentaho.reporting.engine.classic.core.layout.process.util.OrphanPassThroughContext;
/**
* Computes break positions that prevent Orphan and Widow elements, according to the definitions on
* the boxes themselves.
* <p/>
* An Orphan is an element pushed on its own page, with all other elements on the previous pages. This is commonly
* found in groups where the group-footer is pushed to the next page.
* <p/>
* An Widow is an element left on the current page, where all other elements are pushed to the next page. This
* is commonly found for group-headers, where the group-body is pushed to the next page.
* <p/>
* This step calculates the minimum required space that an element would consume if it honours the widow and
* orphan rules.
* <p/>
* When computing the rules, all children are considered, as long as they do not opt-out of the processing. A box
* that opts out, has the 'widow-orphan-opt-out' flag set to true. In the simple set of rules, only block-level
* elements are considered to opt-in for widow and orphan processing.
* <p/>
* For orphans, this step computes the minimum space the element requires to be safely placed on this page. If the
* elements occupying that space would trigger a manual page-break, the break overrides the orphan rule, and the
* space for the orphan processing is limited to the point of the manual break.
* <p/>
* For widows, this step also computes the minimum space required to satisfy the constraint. Manual breaks override
* the widow constraint. During pagination, the pagination processor has to check all parents to see whether their
* widow constrains are still fulfilled.
* <p/>
* If the sum of the widow and orphan constraints is larger than the computed size of the box, the box is considered
* unbreakable and behaves as if the "keep-together" flag has been set.
* <p/>
* The widow-orphan calculation ignores the 'fixed-position' setting when calculating constraints. Combining a
* widow-orphan constraint with the fixed-position constrained yields undefined results. The widow and orphan constraint
* is only active for paginated reports. It has no effect on flow or streaming report outputs.
*/
public class OrphanStep extends IterateSimpleStructureProcessStep
{
private OrphanContext context;
private OrphanContextPool contextPool;
private OrphanPassThroughContext rootContext;
private boolean invalidNodeFound;
public OrphanStep()
{
contextPool = new OrphanContextPool();
rootContext = new OrphanPassThroughContext();
}
public boolean processOrphanAnnotation(final LogicalPageBox box)
{
invalidNodeFound = false;
context = rootContext;
startProcessing(box.getContentArea());
context = null;
return invalidNodeFound;
}
protected void processParagraphChilds(final ParagraphRenderBox box)
{
processBoxChilds(box);
}
protected boolean startBox(final RenderBox box)
{
box.setInvalidWidowOrphanNode(false);
box.setRestrictFinishedClearOut(RenderBox.RestrictFinishClearOut.UNRESTRICTED);
final StaticBoxLayoutProperties properties = box.getStaticBoxLayoutProperties();
if (properties.isWidowOrphanOptOut() == false)
{
context.startChild(box);
}
if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_BREAKMARK)
{
context.registerBreakMark(box);
}
context = contextPool.create(box, context);
return true;
}
protected void processOtherNode(final RenderNode node)
{
if (node instanceof FinishedRenderNode)
{
final FinishedRenderNode finNode = (FinishedRenderNode) node;
if (finNode.isOrphanLeaf())
{
context.registerFinishedNode(finNode);
// feed information about the collapsed node into the parent to have a consistent pagination run.
}
}
}
protected void finishBox(final RenderBox box)
{
final OrphanContext oldContext = context;
context = oldContext.commit(box);
contextPool.free(oldContext);
if (box.isInvalidWidowOrphanNode())
{
invalidNodeFound = true;
}
final StaticBoxLayoutProperties properties = box.getStaticBoxLayoutProperties();
if (properties.isWidowOrphanOptOut() == false)
{
context.endChild(box);
}
}
}