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