net.sf.jasperreports.engine.fill.JRFillCellContents Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jasperreports Show documentation
Show all versions of jasperreports Show documentation
Free Java Reporting Library
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see .
*/
package net.sf.jasperreports.engine.fill;
import java.awt.Color;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections4.map.ReferenceMap;
import net.sf.jasperreports.crosstabs.JRCellContents;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabObjectFactory;
import net.sf.jasperreports.crosstabs.type.CrosstabColumnPositionEnum;
import net.sf.jasperreports.crosstabs.type.CrosstabRowPositionEnum;
import net.sf.jasperreports.engine.JRDefaultStyleProvider;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRFrame;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JROrigin;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintFrame;
import net.sf.jasperreports.engine.JRPropertiesHolder;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRStyleSetter;
import net.sf.jasperreports.engine.export.HtmlExporter;
import net.sf.jasperreports.engine.type.ModeEnum;
/**
* Crosstab cell contents filler.
*
* @author Lucian Chirita ([email protected])
*/
public class JRFillCellContents extends JRFillElementContainer implements JRCellContents, JRStyleSetter
{
public static final String EXCEPTION_MESSAGE_KEY_CANNOT_SHRINK_CONTENTS = "fill.cell.contents.cannot.shrink.contents";
private final Map transformedContentsCache;
private final Map boxContentsCache;
private final JRClonePool clonePool;
private final JROriginProvider originProvider;
private final PrintElementOriginator printElementOriginator;
private JRFillCellContents original;
private final JRCellContents parentCell;
private final String cellType;
private JRLineBox lineBox;
private int height;
private int width;
private int x;
private int y;
private int verticalSpan;
private CrosstabRowPositionEnum verticalPositionType = CrosstabRowPositionEnum.TOP;
private int horizontalSpan;
private Map templateFrames;
private JRDefaultStyleProvider defaultStyleProvider;
private JRStyle initStyle;
private int prepareStretchHeight;
private Map printProperties = new HashMap();
public JRFillCellContents(JRBaseFiller filler, JRCellContents cell, String cellType,
JRFillCrosstabObjectFactory factory)
{
super(filler, cell, factory);
defaultStyleProvider = factory.getDefaultStyleProvider();
parentCell = cell;
this.cellType = cellType;
int elementId = filler.getFillContext().generateFillElementId();
printElementOriginator = new DefaultPrintElementOriginator(elementId);
lineBox = cell.getLineBox().clone(this);
width = cell.getWidth();
height = cell.getHeight();
factory.registerDelayedStyleSetter(this, parentCell);
initElements();
initConditionalStyles();
initTemplatesMap();
this.originProvider = factory.getParentOriginProvider();
setElementOriginProvider(this.originProvider);
transformedContentsCache = new ReferenceMap();
boxContentsCache = new HashMap();
clonePool = new JRClonePool(this, true, true);
}
private void initTemplatesMap()
{
templateFrames = new HashMap();
}
protected JRFillCellContents(JRFillCellContents cellContents, JRFillCloneFactory factory)
{
super(cellContents, factory);
defaultStyleProvider = cellContents.defaultStyleProvider;
parentCell = cellContents.parentCell;
cellType = cellContents.cellType;
printElementOriginator = cellContents.printElementOriginator;
lineBox = cellContents.getLineBox().clone(this);
width = cellContents.width;
height = cellContents.height;
initStyle = cellContents.initStyle;
initElements();
initConditionalStyles();
this.templateFrames = cellContents.templateFrames;
this.originProvider = cellContents.originProvider;
transformedContentsCache = new ReferenceMap();
boxContentsCache = new HashMap();
clonePool = new JRClonePool(this, true, true);
verticalPositionType = cellContents.verticalPositionType;
}
@Override
public Color getBackcolor()
{
return parentCell.getBackcolor();
}
@Override
public JRLineBox getLineBox()
{
return lineBox;
}
/**
*
*/
protected void setBox(JRLineBox box)
{
this.lineBox = box;
initTemplatesMap();
}
@Override
public int getHeight()
{
return height;
}
@Override
public int getWidth()
{
return width;
}
public JRFillCellContents getBoxContents(boolean left, boolean right, boolean top)
{
if (lineBox == null)
{
return this;
}
boolean copyLeft = left && lineBox.getLeftPen().getLineWidth() <= 0f && lineBox.getRightPen().getLineWidth() > 0f;
boolean copyRight = right && lineBox.getRightPen().getLineWidth() <= 0f && lineBox.getLeftPen().getLineWidth() > 0f;
boolean copyTop = top && lineBox.getTopPen().getLineWidth() <= 0f && lineBox.getBottomPen().getLineWidth() > 0f;
if (!(copyLeft || copyRight || copyTop))
{
return this;
}
Object key = new BoxContents(copyLeft, copyRight, copyTop);
JRFillCellContents boxContents = boxContentsCache.get(key);
if (boxContents == null)
{
boxContents = (JRFillCellContents) createClone();
JRLineBox newBox = lineBox.clone(this);
if (copyLeft)
{
newBox.copyLeftPen(lineBox.getRightPen());
}
if (copyRight)
{
newBox.copyRightPen(lineBox.getLeftPen());
}
if (copyTop)
{
newBox.copyTopPen(lineBox.getBottomPen());
}
boxContents.setBox(newBox);
boxContentsCache.put((BoxContents)key, boxContents);
}
return boxContents;
}
public JRFillCellContents getTransformedContents(
int newWidth, int newHeight,
CrosstabColumnPositionEnum xPosition, CrosstabRowPositionEnum yPosition) throws JRException
{
if ((getHeight() == newHeight) &&
(getWidth() == newWidth))
{
return this;
}
if (newHeight < getHeight() || newWidth < getWidth())
{
throw
new JRException(
EXCEPTION_MESSAGE_KEY_CANNOT_SHRINK_CONTENTS,
(Object[])null
);
}
StretchedContents key = new StretchedContents(newWidth, newHeight, xPosition, yPosition);
JRFillCellContents transformedCell = transformedContentsCache.get(key);
if (transformedCell == null)
{
transformedCell = (JRFillCellContents) createClone();
transformedCell.transform(newWidth, newHeight, xPosition, yPosition);
transformedContentsCache.put(key, transformedCell);
}
return transformedCell;
}
private void transform(int newWidth, int newHeight, CrosstabColumnPositionEnum xPosition, CrosstabRowPositionEnum yPosition)
{
transformElements(newWidth, newHeight, xPosition, yPosition);
width = newWidth;
height = newHeight;
}
private void transformElements(int newWidth, int newHeight, CrosstabColumnPositionEnum xPosition, CrosstabRowPositionEnum yPosition)
{
if ((height == newHeight || yPosition == CrosstabRowPositionEnum.TOP) &&
(width == newWidth || xPosition == CrosstabColumnPositionEnum.LEFT))
{
return;
}
double scaleX = -1d;
int offsetX = 0;
switch (xPosition)
{
case CENTER:
offsetX = (newWidth - width) / 2;
break;
case RIGHT:
offsetX = newWidth - width;
break;
case STRETCH:
scaleX = ((double) newWidth) / width;
break;
}
double scaleY = -1d;
int offsetY = 0;
switch (yPosition)
{
case MIDDLE:
offsetY = (newHeight - height) / 2;
break;
case BOTTOM:
offsetY = newHeight - height;
break;
case STRETCH:
scaleY = ((double) newHeight) / height;
break;
}
transformElements(getElements(), scaleX, offsetX, scaleY, offsetY);
}
private static void transformElements(JRElement[] elements, double scaleX, int offsetX, double scaleY, int offsetY)
{
if (elements != null)
{
for (int i = 0; i < elements.length; i++)
{
JRFillElement element = (JRFillElement) elements[i];
if (scaleX != -1d)
{
element.setX((int) (element.getX() * scaleX));
element.setWidth((int) (element.getWidth() * scaleX));
}
if (offsetX != 0)
{
element.setX(element.getX() + offsetX);
}
if (scaleY != -1d)
{
element.setY((int) (element.getY() * scaleY));
element.setHeight((int) (element.getHeight() * scaleY));
}
if (offsetY != 0)
{
element.setY(element.getY() + offsetY);
}
if (element instanceof JRFrame)
{
JRElement[] frameElements = ((JRFrame) element).getElements();
transformElements(frameElements, scaleX, offsetX, scaleY, offsetY);
}
}
}
}
protected void prepare(int availableHeight) throws JRException
{
initFill();
resetElements();
prepareElements(availableHeight, true);
// store the original stretch height in order to compute the external
// stretch height
prepareStretchHeight = getStretchHeight();
}
public void setPrintProperty(String name, String value)
{
printProperties.put(name, value);
}
protected JRPrintFrame fill() throws JRException
{
if (isLegacyElementStretchEnabled())
{
stretchElements();
moveBandBottomElements();
removeBlankElements();
}
else
{
stretchElementsToContainer();
moveBandBottomElements();
}
JRTemplatePrintFrame printCell = new JRTemplatePrintFrame(getTemplateFrame(), printElementOriginator);
//printCell.setUUID();
printCell.setX(x);
printCell.setY(y);
printCell.setWidth(width);
fillElements(printCell);
verticallyPositionElements(printCell);
printCell.setHeight(getPrintHeight());
setCellProperties(printCell);
return printCell;
}
protected void setCellProperties(JRTemplatePrintFrame printCell)
{
if (verticalSpan > 1)
{
printCell.getPropertiesMap().setProperty(
JRCellContents.PROPERTY_ROW_SPAN, Integer.toString(verticalSpan));
}
if (horizontalSpan > 1)
{
printCell.getPropertiesMap().setProperty(
JRCellContents.PROPERTY_COLUMN_SPAN, Integer.toString(horizontalSpan));
}
// TODO lucianc find a way to put this in templates
for (Entry propEntry : printProperties.entrySet())
{
printCell.getPropertiesMap().setProperty(propEntry.getKey(), propEntry.getValue());
}
// not transferring cell properties for now
}
protected JRTemplateFrame getTemplateFrame()
{
JRStyle style = getStyle();
JRTemplateFrame template = templateFrames.get(style);
if (template == null)
{
template = new JRTemplateFrame(getOrigin(),
filler.getJasperPrint().getDefaultStyleProvider(), this);
template.getPropertiesMap().setProperty(
JRCellContents.PROPERTY_TYPE, cellType);
template = filler.fillContext.deduplicate(template);
templateFrames.put(style, template);
}
return template;
}
protected JROrigin getOrigin()
{
return originProvider == null ? null : originProvider.getOrigin();
}
protected void verticallyPositionElements(JRTemplatePrintFrame printCell)
{
int positionOffset;
switch (verticalPositionType)
{
case MIDDLE:
positionOffset = (getStretchHeight() - prepareStretchHeight) / 2;
break;
case BOTTOM:
positionOffset = getStretchHeight() - prepareStretchHeight;
break;
default:
positionOffset = 0;
break;
}
if (positionOffset != 0)
{
List printElements = printCell.getElements();
int positionY = getStretchHeight() - positionOffset;
boolean outside = false;
for (Iterator it = printElements.iterator(); !outside && it.hasNext();)
{
JRPrintElement element = it.next();
outside = element.getY() + element.getHeight() > positionY;
}
if (!outside)
{
for (Iterator it = printElements.iterator(); it.hasNext();)
{
JRPrintElement element = it.next();
element.setY(element.getY() + positionOffset);
}
}
}
}
protected int getPrintHeight()
{
return getStretchHeight() + getTopPadding() + getBottomPadding();
}
protected void stretchTo(int stretchHeight)
{
setStretchHeight(stretchHeight - getTopPadding() - getBottomPadding());
}
protected static class BoxContents
{
final boolean left;
final boolean right;
final boolean top;
final int hashCode;
public BoxContents(boolean left, boolean right, boolean top)
{
this.left = left;
this.right = right;
this.top = top;
int hash = left ? 1231 : 1237;
hash = 31*hash + (right ? 1231 : 1237);
hash = 31*hash + (top ? 1231 : 1237);
hashCode = hash;
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
BoxContents b = (BoxContents) obj;
return
b.left == left && b.right == right && b.top == top;
}
@Override
public int hashCode()
{
return hashCode;
}
}
protected static class StretchedContents
{
final int newHeight;
final int newWidth;
final int hashCode;
final CrosstabColumnPositionEnum xPosition;
final CrosstabRowPositionEnum yPosition;
StretchedContents(
int newWidth, int newHeight, CrosstabColumnPositionEnum xPosition, CrosstabRowPositionEnum yPosition)
{
this.newHeight = newHeight;
this.newWidth = newWidth;
this.xPosition = xPosition;
this.yPosition = yPosition;
int hash = newHeight;
hash = 31*hash + newWidth;
hash = 31*hash + xPosition.ordinal();
hash = 31*hash + yPosition.ordinal();
hashCode = hash;
}
@Override
public boolean equals(Object o)
{
if (o == this)
{
return true;
}
StretchedContents s = (StretchedContents) o;
return
s.newHeight == newHeight && s.newWidth == newWidth &&
s.xPosition == xPosition && s.yPosition == yPosition;
}
@Override
public int hashCode()
{
return hashCode;
}
}
@Override
protected int getContainerHeight()
{
return getHeight() - getTopPadding() - getBottomPadding();
}
@Override
protected int getActualContainerHeight()
{
return getContainerHeight();
}
protected int getTopPadding()
{
return lineBox == null ? 0 : lineBox.getTopPadding();
}
protected int getBottomPadding()
{
return lineBox == null ? 0 : lineBox.getBottomPadding();
}
public JRFillCloneable createClone()
{
JRFillCloneFactory factory = new JRFillCloneFactory();
return createClone(factory);
}
@Override
public JRFillCloneable createClone(JRFillCloneFactory factory)
{
return new JRFillCellContents(this, factory);
}
public JRFillCellContents getWorkingClone()
{
JRFillCellContents clone = (JRFillCellContents) clonePool.getClone();
clone.original = this;
return clone;
}
public void releaseWorkingClone()
{
original.clonePool.releaseClone(this);
}
public void setX(int x)
{
this.x = x;
}
public void setY(int y)
{
this.y = y;
}
public int getVerticalSpan()
{
return verticalSpan;
}
public void setVerticalSpan(int span)
{
verticalSpan = span;
}
public void setVerticalPositionType(CrosstabRowPositionEnum positionType)
{
this.verticalPositionType = positionType;
}
public int getHorizontalSpan()
{
return horizontalSpan;
}
public void setHorizontalSpan(int horizontalSpan)
{
this.horizontalSpan = horizontalSpan;
}
@Override
protected void evaluate(byte evaluation) throws JRException
{
printProperties.clear();
evaluateConditionalStyles(evaluation);
super.evaluate(evaluation);
}
@Override
public JRDefaultStyleProvider getDefaultStyleProvider()
{
return defaultStyleProvider;
}
@Override
public JRStyle getStyle()
{
JRStyle crtStyle = initStyle;
boolean isUsingDefaultStyle = false;
if (crtStyle == null)
{
crtStyle = filler.getDefaultStyle();
isUsingDefaultStyle = true;
}
JRStyle evalStyle = getEvaluatedConditionalStyle(crtStyle);
if (isUsingDefaultStyle && evalStyle == crtStyle)
{
evalStyle = null;
}
return evalStyle;
}
@Override
protected void initConditionalStyles()
{
super.initConditionalStyles();
collectConditionalStyle(initStyle);
}
@Override
public ModeEnum getModeValue()
{
return parentCell.getModeValue();
}
@Override
public String getStyleNameReference()
{
return null;
}
@Override
public void setStyle(JRStyle style)
{
this.initStyle = style;
collectConditionalStyle(style);
}
@Override
public void setStyleNameReference(String name)
{
throw new UnsupportedOperationException("Style name references not allowed at fill time");
}
@Override
public Color getDefaultLineColor()
{
return parentCell.getDefaultLineColor();
}
@Override
public boolean hasProperties()
{
return parentCell.hasProperties();
}
// not doing anything with the properties at fill time
@Override
public JRPropertiesMap getPropertiesMap()
{
return parentCell.getPropertiesMap();
}
@Override
public JRPropertiesHolder getParentProperties()
{
return null;
}
protected void addElement(int index, JRFillElement element)
{
children.add(index, element);
// recreate elements array
this.elements = null;
getElements();
initDeepElements();
// we need to reinit elements
initElements();
// assuming that the element is not deep and that it does not bring new conditional styles
element.setConditionalStylesContainer(this);
element.setOriginProvider(originProvider);
}
protected void addHtmlClass(String className)
{
String originalClasses = printProperties.get(HtmlExporter.PROPERTY_HTML_CLASS);
String newClasses = originalClasses == null ? className : (originalClasses + " " + className);
setPrintProperty(HtmlExporter.PROPERTY_HTML_CLASS, newClasses);
}
@Override
public boolean isSplitTypePreventInhibited(boolean isTopLevelCall)
{
//FIXME implement logic
return false;
}
}