/* * 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) 2000 - 2013 Pentaho Corporation and Contributors... * All rights reserved. */ package org.pentaho.reporting.engine.classic.core.layout.build; import org.pentaho.reporting.engine.classic.core.layout.model.InlineProgressMarkerRenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes; import org.pentaho.reporting.engine.classic.core.layout.model.ProgressMarkerRenderBox; 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.states.ReportStateKey; import org.pentaho.reporting.engine.classic.core.style.StyleSheet; import org.pentaho.reporting.engine.classic.core.util.InstanceID; public class GroupSection implements Cloneable { private static class FlatGroupSectionStrategy implements Cloneable { protected static final double COMMON_GROWTH = 0.5; protected static final int INITIAL_COMMON_SIZE = 50; protected static final int MAXIMUM_COMMON_SIZE = 5000; private int childCount; private int nextBoxStart; private StyleSheet styleSheet; private RenderBox addBox; private RenderBox groupBox; private FlatGroupSectionStrategy( final RenderBox addBox, final StyleSheet styleSheet ) { this.groupBox = addBox; this.addBox = addBox; this.styleSheet = styleSheet; this.childCount = 0; this.nextBoxStart = INITIAL_COMMON_SIZE; } protected void initGrowth( final int growth ) { this.nextBoxStart = growth; } public Object clone() { try { return super.clone(); } catch ( CloneNotSupportedException cse ) { throw new IllegalStateException( cse ); } } public boolean mergeSection( final ReportStateKey stateKey ) { final RenderNode lastSection = getAddBox().getLastChild(); if ( lastSection == null ) { return false; } if ( ( lastSection.getLayoutNodeType() & LayoutNodeTypes.MASK_BOX ) != LayoutNodeTypes.MASK_BOX ) { return false; } final RenderBox lastSectionBox = (RenderBox) lastSection; final RenderNode maybeMarker = lastSectionBox.getLastChild(); if ( maybeMarker == null ) { return false; } final int nodeType = maybeMarker.getNodeType(); if ( nodeType == LayoutNodeTypes.TYPE_BOX_INLINE_PROGRESS_MARKER ) { final InlineProgressMarkerRenderBox markerRenderBox = (InlineProgressMarkerRenderBox) maybeMarker; markerRenderBox.setStateKey( stateKey ); return true; } else if ( nodeType == LayoutNodeTypes.TYPE_BOX_PROGRESS_MARKER ) { final ProgressMarkerRenderBox markerRenderBox = (ProgressMarkerRenderBox) maybeMarker; markerRenderBox.setStateKey( stateKey ); return true; } return false; } public RenderBox getGroupBox() { return groupBox; } public void setAddBox( final RenderBox addBox ) { this.addBox = addBox; } public RenderBox getAddBox() { return addBox; } public int getChildCount() { return childCount; } public void addedSection( final RenderNode node ) { childCount += 1; if ( childCount == nextBoxStart ) { if ( addBox != groupBox ) { addBox.close(); } final RenderBox commonBox = addBox.create( styleSheet ); commonBox.setName( "Common-Section" ); // NON-NLS groupBox.addChild( commonBox ); addBox = commonBox; nextBoxStart += computeGrowth(); } addBox.addChild( node ); } public void removedLastSection( final RenderNode child ) { childCount -= 1; addBox.remove( child ); } protected int computeGrowth() { if ( nextBoxStart == 0 ) { return INITIAL_COMMON_SIZE; } return (int) Math.min( MAXIMUM_COMMON_SIZE, nextBoxStart * COMMON_GROWTH ); } public void close() { if ( addBox != groupBox ) { addBox.close(); } groupBox.close(); } public FlatGroupSectionStrategy deriveForStorage( final RenderBox clonedParent ) { final FlatGroupSectionStrategy clone = (FlatGroupSectionStrategy) clone(); final InstanceID groupBoxInstanceId = groupBox.getInstanceId(); final RenderBox groupBoxClone; if ( clonedParent == null ) { groupBoxClone = (RenderBox) clone.groupBox.derive( true ); } else { groupBoxClone = (RenderBox) clonedParent.findNodeById( groupBoxInstanceId ); if ( groupBoxClone == null ) { throw new IllegalStateException( "The pagebox did no longer contain the stored node." ); } if ( groupBoxClone == groupBox ) { throw new IllegalStateException( "Thought you wanted a groupBoxClone" ); } } final RenderBox addBox = getAddBox(); final RenderBox addBoxClone; if ( addBox == groupBox ) { addBoxClone = groupBoxClone; } else { final InstanceID addBoxInstanceId = addBox.getInstanceId(); addBoxClone = (RenderBox) groupBoxClone.findNodeById( addBoxInstanceId ); if ( addBoxClone == null ) { throw new IllegalStateException( "The pagebox did no longer contain the stored node." ); } if ( addBoxClone == addBox ) { throw new IllegalStateException( "Thought you wanted a groupBoxClone" ); } } clone.addBox = addBoxClone; clone.groupBox = groupBoxClone; return clone; } public void restoreStateAfterRollback() { if ( addBox.isOpen() == false && addBox.getParent() == null ) { addBox = groupBox.create( styleSheet ); } } } private static class QuadraticGroupSection extends FlatGroupSectionStrategy { private int predictedGrowth; private int predictedSize; public QuadraticGroupSection( final RenderBox groupBox, final StyleSheet styleSheet, final int predictedSize ) { super( groupBox, styleSheet ); this.predictedSize = predictedSize; this.predictedGrowth = (int) Math.ceil( Math.pow( predictedSize, 0.4 ) ); final RenderBox commonBox = groupBox.create( styleSheet ); commonBox.setName( "Common-Section" ); // NON-NLS setAddBox( commonBox ); getGroupBox().addChild( getAddBox() ); initGrowth( Math.max( 5, predictedGrowth ) ); } public void addedSection( final RenderNode node ) { super.addedSection( node ); // if ( getAddBox().getParent() == null ) { // getGroupBox().addChild(getAddBox()); // } } protected int computeGrowth() { return predictedGrowth; } } private static class LegacyGroupSectionStrategy extends FlatGroupSectionStrategy { private static final int INITIAL_COMMON_SIZE = 50; private int predictedGrowth; private int predictedSize; protected LegacyGroupSectionStrategy( final RenderBox groupBox, final StyleSheet styleSheet, final int predictedSize ) { super( groupBox, styleSheet ); this.predictedSize = predictedSize; if ( predictedSize == 0 ) { initGrowth( INITIAL_COMMON_SIZE ); } else { predictedGrowth = (int) Math.max( 5, Math.sqrt( predictedSize ) ); initGrowth( predictedGrowth ); } } protected int computeGrowth() { if ( predictedSize == 0 || getChildCount() > predictedSize ) { return (int) Math.min( MAXIMUM_COMMON_SIZE, getChildCount() * COMMON_GROWTH ); } return predictedGrowth; } } private StyleSheet styleSheet; private FlatGroupSectionStrategy strategy; public GroupSection( final RenderBox groupBox, final StyleSheet styleSheet, final int predictedSize, final boolean legacyMode ) { if ( groupBox == null ) { throw new NullPointerException(); } this.styleSheet = styleSheet; if ( predictedSize <= 15 ) { this.strategy = new FlatGroupSectionStrategy( groupBox, styleSheet ); } else { if ( legacyMode ) { this.strategy = new LegacyGroupSectionStrategy( groupBox, styleSheet, 0 ); } else { this.strategy = new QuadraticGroupSection( groupBox, styleSheet, predictedSize ); } } } public boolean mergeSection( final ReportStateKey stateKey ) { return strategy.mergeSection( stateKey ); } public void addedSection( final RenderNode node ) { strategy.addedSection( node ); } public void removedLastSection( final RenderNode child ) { strategy.removedLastSection( child ); } public void close() { strategy.close(); } public RenderBox getGroupBox() { return strategy.getGroupBox(); } public boolean isEmpty() { return getChildCount() == 0; } public int getChildCount() { return strategy.getChildCount(); } public StyleSheet getStyleSheet() { return styleSheet; } public Object clone() { try { return super.clone(); } catch ( CloneNotSupportedException e ) { throw new IllegalStateException( e ); } } public GroupSection deriveForPagebreak() { final GroupSection clone = (GroupSection) clone(); clone.strategy = (FlatGroupSectionStrategy) strategy.clone(); return clone; } public GroupSection deriveForStorage( final RenderBox clonedParent ) { final GroupSection clone = (GroupSection) clone(); clone.strategy = strategy.deriveForStorage( clonedParent ); return clone; } public void performParanoidModelCheck() { // step 1: Check whether addbox is a child of groupbox RenderBox c = strategy.getAddBox(); if ( strategy.getChildCount() > 0 ) { while ( c != strategy.getGroupBox() ) { c = c.getParent(); if ( c == null ) { throw new IllegalStateException( "Failed to locate parent" ); } } } c = strategy.getAddBox(); while ( c != null ) { if ( c.isOpen() == false ) { throw new IllegalStateException( "Add-Box is not open: " + c.isMarkedOpen() + ' ' + c.isMarkedSeen() + ' ' + c ); } c = c.getParent(); } } public void performPostCommitModelCheck() { final RenderBox groupBox = strategy.getGroupBox(); if ( groupBox.isMarkedSeen() == false ) { throw new IllegalStateException( "No seen-marker at " + groupBox ); } if ( strategy.getChildCount() > 0 ) { final RenderBox addBox = strategy.getAddBox(); if ( addBox.getParent() == null ) { throw new IllegalStateException( "No longer there" ); } if ( addBox.isMarkedSeen() == false ) { throw new IllegalStateException( "No seen-marker at add-box " + addBox ); } if ( addBox.isMarkedOpen() == false ) { throw new IllegalStateException( "No open-marker at " + addBox ); } } } public void restoreStateAfterRollback() { strategy.restoreStateAfterRollback(); } public RenderBox getAddBox() { return strategy.getAddBox(); } }