/*! * 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.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.engine.classic.core.layout.model.FinishedRenderNode; 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.util.RingBuffer; public class OrphanBlockContext implements OrphanContext { private static final Log logger = LogFactory.getLog( OrphanBlockContext.class ); private StackedObjectPool<OrphanBlockContext> pool; private OrphanContext parent; private RenderBox contextBox; private int orphans; private int orphanCount; private RingBuffer<RenderNode> orphanSize; private long orphanOverride; private RenderNode currentNode; private boolean breakMarkerSeen; public OrphanBlockContext() { } public void init( final StackedObjectPool<OrphanBlockContext> pool, final OrphanContext parent, final RenderBox contextBox, final int orphans ) { this.breakMarkerSeen = false; this.pool = pool; this.parent = parent; this.contextBox = contextBox; this.orphans = orphans; this.orphanOverride = contextBox.getCachedY(); this.orphanCount = 0; if ( orphans > 0 ) { if ( this.orphanSize == null ) { this.orphanSize = new RingBuffer<RenderNode>( orphans ); } else { this.orphanSize.resize( orphans ); } } } public void startChild( final RenderBox box ) { currentNode = box; if ( parent != null ) { parent.startChild( box ); } } public void endChild( final RenderBox box ) { if ( currentNode != null ) { if ( orphanCount < orphans && orphans > 0 ) { orphanSize.add( box ); box.setRestrictFinishedClearOut( RenderBox.RestrictFinishClearOut.LEAF ); } orphanCount += 1; currentNode = null; } if ( parent != null ) { parent.endChild( box ); } } public void registerFinishedNode( final FinishedRenderNode box ) { if ( orphanCount < orphans && orphans > 0 ) { orphanSize.add( box ); box.getParent().setRestrictFinishedClearOut( RenderBox.RestrictFinishClearOut.RESTRICTED ); } orphanCount += box.getOrphanLeafCount(); currentNode = null; if ( parent != null ) { parent.registerFinishedNode( box ); } } public void registerBreakMark( final RenderBox box ) { breakMarkerSeen = true; if ( parent != null ) { parent.registerBreakMark( box ); } } public long getOrphanValue() { if ( orphans == 0 ) { return orphanOverride; } final RenderNode lastValue = orphanSize.getLastValue(); if ( lastValue == null ) { return orphanOverride; } return Math.max( orphanOverride, lastValue.getCachedY2() ); } public OrphanContext commit( final RenderBox box ) { final boolean keepTogether = box.getStaticBoxLayoutProperties().isAvoidPagebreakInside(); final long constraintSize; if ( keepTogether ) { constraintSize = Math.max( getOrphanValue(), box.getCachedY() + box.getCachedHeight() ); } else { constraintSize = getOrphanValue(); } box.setOrphanConstraintSize( Math.max( 0, constraintSize - box.getCachedY() ) ); box.setOrphanLeafCount( orphanCount ); final boolean incomplete = box.isOpen() || box.getContentRefCount() > 0; if ( breakMarkerSeen == false && incomplete ) { if ( orphanCount < orphans || keepTogether ) { // the box is either open or has an open sub-report and the orphan constraint is not fulfilled. // also block if there is an overlap between the orphan range and the widow range. box.setInvalidWidowOrphanNode( true ); } else { box.setInvalidWidowOrphanNode( false ); } } else { // the box is safe to process box.setInvalidWidowOrphanNode( false ); } if ( parent != null ) { parent.subContextCommitted( box ); } return parent; } public void subContextCommitted( final RenderBox contextBox ) { // if there is overlap between the child context and the current lock-out area, process it. // also process it if the overlap area is currently empty and the box's upper edges match. final long cachedY = contextBox.getCachedY(); if ( cachedY < getOrphanValue() || ( cachedY == this.contextBox.getCachedY() && cachedY == getOrphanValue() ) ) { orphanOverride = Math.max( orphanOverride, cachedY + contextBox.getOrphanConstraintSize() ); } if ( parent != null ) { parent.subContextCommitted( contextBox ); } } public void clearForPooledReuse() { parent = null; contextBox = null; pool.free( this ); } }