net.sf.jasperreports.components.table.fill.TableReport Maven / Gradle / Ivy
/*
* 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.components.table.fill;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import net.sf.jasperreports.annotations.properties.Property;
import net.sf.jasperreports.annotations.properties.PropertyScope;
import net.sf.jasperreports.components.ComponentsExtensionsRegistryFactory;
import net.sf.jasperreports.components.headertoolbar.HeaderToolbarElement;
import net.sf.jasperreports.components.headertoolbar.HeaderToolbarElementUtils;
import net.sf.jasperreports.components.iconlabel.IconLabelComponent;
import net.sf.jasperreports.components.iconlabel.IconLabelComponentUtil;
import net.sf.jasperreports.components.sort.FieldFilter;
import net.sf.jasperreports.components.sort.FilterTypesEnum;
import net.sf.jasperreports.components.sort.SortElementHtmlHandler;
import net.sf.jasperreports.components.sort.actions.FilterCommand;
import net.sf.jasperreports.components.table.BaseCell;
import net.sf.jasperreports.components.table.BaseColumn;
import net.sf.jasperreports.components.table.Cell;
import net.sf.jasperreports.components.table.Column;
import net.sf.jasperreports.components.table.ColumnGroup;
import net.sf.jasperreports.components.table.ColumnVisitor;
import net.sf.jasperreports.components.table.TableComponent;
import net.sf.jasperreports.components.table.WhenNoDataTypeTableEnum;
import net.sf.jasperreports.components.table.util.TableUtil;
import net.sf.jasperreports.engine.CompositeDatasetFilter;
import net.sf.jasperreports.engine.DatasetFilter;
import net.sf.jasperreports.engine.DatasetPropertyExpression;
import net.sf.jasperreports.engine.JRBand;
import net.sf.jasperreports.engine.JRChild;
import net.sf.jasperreports.engine.JRComponentElement;
import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRElementGroup;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRExpressionChunk;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JROrigin;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRPropertiesHolder;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRQuery;
import net.sf.jasperreports.engine.JRReport;
import net.sf.jasperreports.engine.JRReportTemplate;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRScriptlet;
import net.sf.jasperreports.engine.JRSection;
import net.sf.jasperreports.engine.JRSortField;
import net.sf.jasperreports.engine.JRStaticText;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRTextField;
import net.sf.jasperreports.engine.JRValueParameter;
import net.sf.jasperreports.engine.JRVariable;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.base.JRBaseElement;
import net.sf.jasperreports.engine.base.JRBaseTextElement;
import net.sf.jasperreports.engine.component.FillContext;
import net.sf.jasperreports.engine.design.JRDesignBand;
import net.sf.jasperreports.engine.design.JRDesignElement;
import net.sf.jasperreports.engine.design.JRDesignElementGroup;
import net.sf.jasperreports.engine.design.JRDesignExpression;
import net.sf.jasperreports.engine.design.JRDesignFrame;
import net.sf.jasperreports.engine.design.JRDesignGenericElement;
import net.sf.jasperreports.engine.design.JRDesignGenericElementParameter;
import net.sf.jasperreports.engine.design.JRDesignGroup;
import net.sf.jasperreports.engine.design.JRDesignSection;
import net.sf.jasperreports.engine.design.JRDesignTextField;
import net.sf.jasperreports.engine.export.HtmlExporter;
import net.sf.jasperreports.engine.export.JRPdfExporterTagHelper;
import net.sf.jasperreports.engine.export.MatcherExporterFilter;
import net.sf.jasperreports.engine.fill.DatasetExpressionEvaluator;
import net.sf.jasperreports.engine.fill.JRExpressionEvalException;
import net.sf.jasperreports.engine.fill.JRFillField;
import net.sf.jasperreports.engine.fill.JRFillParameter;
import net.sf.jasperreports.engine.fill.JRFillVariable;
import net.sf.jasperreports.engine.type.BandTypeEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.OrientationEnum;
import net.sf.jasperreports.engine.type.PositionTypeEnum;
import net.sf.jasperreports.engine.type.PrintOrderEnum;
import net.sf.jasperreports.engine.type.RunDirectionEnum;
import net.sf.jasperreports.engine.type.SectionTypeEnum;
import net.sf.jasperreports.engine.type.SortFieldTypeEnum;
import net.sf.jasperreports.engine.type.SortOrderEnum;
import net.sf.jasperreports.engine.type.SplitTypeEnum;
import net.sf.jasperreports.engine.type.StretchTypeEnum;
import net.sf.jasperreports.engine.type.WhenNoDataTypeEnum;
import net.sf.jasperreports.engine.type.WhenResourceMissingTypeEnum;
import net.sf.jasperreports.engine.util.DigestUtils;
import net.sf.jasperreports.engine.util.JRDataUtils;
import net.sf.jasperreports.engine.util.JRStringUtil;
import net.sf.jasperreports.engine.util.Pair;
import net.sf.jasperreports.engine.util.StyleResolver;
import net.sf.jasperreports.engine.util.StyleUtil;
import net.sf.jasperreports.properties.PropertyConstants;
import net.sf.jasperreports.web.util.JacksonUtil;
/**
* @author Lucian Chirita ([email protected])
*/
public class TableReport implements JRReport
{
public static final String EXCEPTION_MESSAGE_KEY_UNKNOWN_CHILD_TYPE = "fill.table.report.unknown.child.type";
public static final String EXCEPTION_MESSAGE_KEY_FIELD_NOT_FOUND = "fill.table.report.field.not.found";
public static final String EXCEPTION_MESSAGE_KEY_VARIABLE_NOT_FOUND = "fill.table.report.variable.not.found";
public static final String EXCEPTION_MESSAGE_KEY_UNRECOGNIZED_FILTER_EXPRESSION_TYPE = "fill.table.report.unrecognized.filter.expression.type";
public static final String METADATA_KEY_QUALIFICATION =
ComponentsExtensionsRegistryFactory.NAMESPACE
+ PropertyConstants.COMPONENT_KEY_QUALIFICATION_SEPARATOR
+ ComponentsExtensionsRegistryFactory.TABLE_COMPONENT_NAME;
/**
* Global property that specifies the character to be used on the column header when the tables's column is sorted ascending
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = "\\u25B2",
scopes = {PropertyScope.CONTEXT},
sinceVersion = PropertyConstants.VERSION_4_6_0
)
public static final String PROPERTY_UP_ARROW_CHAR = JRPropertiesUtil.PROPERTY_PREFIX + "components.sort.up.arrow.char"; //FIXMEJIVE move these from here
/**
* Global property that specifies the character to be used on the column header when the tables's column is sorted descending
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = "\\u25BC",
scopes = {PropertyScope.CONTEXT},
sinceVersion = PropertyConstants.VERSION_4_6_0
)
public static final String PROPERTY_DOWN_ARROW_CHAR = JRPropertiesUtil.PROPERTY_PREFIX + "components.sort.down.arrow.char";
/**
* Global property that specifies the character to be used on the column header when the tables's column has a filtered applied
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = "\\2606",
scopes = {PropertyScope.CONTEXT},
sinceVersion = PropertyConstants.VERSION_4_6_0
)
public static final String PROPERTY_FILTER_CHAR = JRPropertiesUtil.PROPERTY_PREFIX + "components.filter.char";
/**
* Global property that specifies the font to be used for the icons on the column header
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
scopes = {PropertyScope.CONTEXT},
sinceVersion = PropertyConstants.VERSION_5_5_0
)
public static final String PROPERTY_ICON_FONT = JRPropertiesUtil.PROPERTY_PREFIX + "components.icon.font";
/**
* Property that enables/disables the interactivity in the table component
*
*
* The property can be set:
*
* - globally
* - at report level
* - at component level
* - at column level
*
*
*
* The default global value of this property is true
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = PropertyConstants.BOOLEAN_TRUE,
scopes = {PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.COMPONENT, PropertyScope.TABLE_COLUMN},
scopeQualifications = {METADATA_KEY_QUALIFICATION},
sinceVersion = PropertyConstants.VERSION_4_7_1,
valueType = Boolean.class
)
public static final String PROPERTY_INTERACTIVE_TABLE = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.interactive";
/**
* Property that enables/disables the floating header in the table component when scrolling.
* If the interactivity has been disabled by setting {@link #PROPERTY_INTERACTIVE_TABLE} to false
, then
* setting this property will have no effect.
*
*
* It can be set:
*
* - globally
* - at report level
* - at component level
*
*
*
*
* The default global value of this property is true
*
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = PropertyConstants.BOOLEAN_TRUE,
scopes = {PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.COMPONENT},
scopeQualifications = {METADATA_KEY_QUALIFICATION},
sinceVersion = PropertyConstants.VERSION_6_3_1,
valueType = Boolean.class
)
public static final String PROPERTY_FLOATING_HEADER = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.floating.header";
/**
* Property that enables/disables the automatic addition of specific custom properties to table cell elements,
* that would in turn trigger the creation of special document accessibility tags during PDF export
*
*
* The property can be set:
*
* - globally
* - at report level
* - at component level
*
*
*
* The default global value of this property is false
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = PropertyConstants.BOOLEAN_FALSE,
scopes = {PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.COMPONENT},
scopeQualifications = {METADATA_KEY_QUALIFICATION},
sinceVersion = PropertyConstants.VERSION_6_1_2,
valueType = Boolean.class
)
public static final String PROPERTY_GENERATE_TABLE_PDF_TAGS = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.generate.pdf.tags";
/**
* Column property that specifies the field to be used for sorting, filtering and conditional formatting
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
scopes = {PropertyScope.TABLE_COLUMN},
sinceVersion = PropertyConstants.VERSION_5_0_1
)
public static final String PROPERTY_COLUMN_FIELD = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.column.field";
/**
* Column property that specifies the variable to be used for sorting, filtering and conditional formatting
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
scopes = {PropertyScope.TABLE_COLUMN},
sinceVersion = PropertyConstants.VERSION_5_0_1
)
public static final String PROPERTY_COLUMN_VARIABLE = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.column.variable";
/**
* Column property that enables/disables sorting
*
*
* It defaults to true
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = PropertyConstants.BOOLEAN_TRUE,
scopes = {PropertyScope.TABLE_COLUMN},
sinceVersion = PropertyConstants.VERSION_5_0_1,
valueType = Boolean.class
)
public static final String PROPERTY_COLUMN_SORTABLE = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.column.sortable";
/**
* Column property that enables/disables filtering
*
*
* It defaults to true
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = PropertyConstants.BOOLEAN_TRUE,
scopes = {PropertyScope.TABLE_COLUMN},
sinceVersion = PropertyConstants.VERSION_5_0_1,
valueType = Boolean.class
)
public static final String PROPERTY_COLUMN_FILTERABLE = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.column.filterable";
/**
* Column property that enables/disables conditional formatting
*
*
* It defaults to true
* @deprecated To be removed.
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
defaultValue = PropertyConstants.BOOLEAN_TRUE,
scopes = {PropertyScope.TABLE_COLUMN},
sinceVersion = PropertyConstants.VERSION_5_0_1,
valueType = Boolean.class
)
public static final String PROPERTY_COLUMN_CONDITIONALLY_FORMATTABLE = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.column.conditionally.formattable";
/**
* Property that provides a name for table.
*
*
* The name is used as report name in element origins.
*
* @see JROrigin#getReportName()
*/
@Property(
category = PropertyConstants.CATEGORY_TABLE,
scopes = {PropertyScope.COMPONENT},
scopeQualifications = {METADATA_KEY_QUALIFICATION},
sinceVersion = PropertyConstants.VERSION_6_6_0
)
public static final String PROPERTY_TABLE_NAME = JRPropertiesUtil.PROPERTY_PREFIX + "components.table.name";
protected static final String SUMMARY_GROUP_NAME = "__SummaryGroup";
protected static final String HTML_CLASS_CELL_PREFIX = "cel_";
protected static final String HTML_CLASS_CELL = "jrcel";
/**
*
*/
public static final String TABLE_HEADER_LABEL_MATCHER_EXPORT_KEY = "net.sf.jasperreports.components.table.header.label";
public static final String TABLE_HEADER_ICON_LABEL_MATCHER_EXPORT_KEY = "net.sf.jasperreports.components.table.header.icon.label";
private final FillContext fillContext;
private final TableComponent table;
private final JasperReport parentReport;
private final TableReportDataset mainDataset;
private final BuiltinExpressionEvaluatorFactory builtinEvaluatorFactory;
private final JRSection detail;
private final JRDesignBand title;
private final JRDesignBand summary;
private final JRDesignBand columnHeader;
private final JRDesignBand pageFooter;
private final JRDesignBand lastPageFooter;
private final JRDesignBand noData;
private final List tableIndexProperties;
private final Map headerHtmlBaseProperties;
private final JRPropertiesUtil propertiesUtil;
private String tableName;
private boolean isInteractiveTable;
private boolean hasFloatingHeader;
private boolean isGeneratePdfTags;
private Map> columnInteractivityMapping;
public TableReport(
FillContext fillContext,
TableComponent table,
TableReportDataset mainDataset,
List fillColumns,
BuiltinExpressionEvaluatorFactory builtinEvaluatorFactory
)
{
this.tableIndexProperties = new ArrayList();
this.headerHtmlBaseProperties = new HashMap();
this.fillContext = fillContext;
this.table = table;
this.parentReport = fillContext.getFiller().getJasperReport();
this.mainDataset = mainDataset;
this.builtinEvaluatorFactory = builtinEvaluatorFactory;
this.tableName = JRPropertiesUtil.getOwnProperty(fillContext.getComponentElement(), PROPERTY_TABLE_NAME);
this.propertiesUtil = JRPropertiesUtil.getInstance(fillContext.getFiller().getJasperReportsContext());
// begin: table interactivity
this.isInteractiveTable = Boolean.valueOf(propertiesUtil.getProperty(PROPERTY_INTERACTIVE_TABLE, fillContext.getComponentElement(), this.parentReport));
this.columnInteractivityMapping = new HashMap>();
int interactiveColumnCount = 0;
for (BaseColumn column: TableUtil.getAllColumns(table)) {
boolean interactiveColumn = isInteractiveTable;
String columnName = null;
if (column.getPropertiesMap().containsProperty(PROPERTY_INTERACTIVE_TABLE)) {
interactiveColumn = Boolean.valueOf(column.getPropertiesMap().getProperty(PROPERTY_INTERACTIVE_TABLE));
}
if (interactiveColumn) {
interactiveColumnCount++;
}
if (column.getPropertiesMap().containsProperty(JRComponentElement.PROPERTY_COMPONENT_NAME)) {
columnName = column.getPropertiesMap().getProperty(JRComponentElement.PROPERTY_COMPONENT_NAME);
}
columnInteractivityMapping.put((Column)column, new Pair(interactiveColumn, columnName));
}
if (interactiveColumnCount > 0) {
this.isInteractiveTable = true;
this.hasFloatingHeader = propertiesUtil.getBooleanProperty(PROPERTY_FLOATING_HEADER, true, fillContext.getComponentElement(), this.parentReport);
}
// end: table interactivity
this.isGeneratePdfTags = Boolean.valueOf(propertiesUtil.getProperty(PROPERTY_GENERATE_TABLE_PDF_TAGS, fillContext.getComponentElement(), this.parentReport));
this.columnHeader = createColumnHeader(fillColumns);
this.detail = wrapBand(createDetailBand(fillColumns), new JROrigin(BandTypeEnum.DETAIL));
this.title = createTitle(fillColumns);
this.summary = createSummary(fillColumns);
this.pageFooter = createPageFooter(fillColumns);
setGroupBands(fillColumns);
if (pageFooter != null && summary != null)
{
// if the table has both column footers and table footers, we need to use
// a dummy group's footer to print the last column footers so that they
// appear before the table footers
addSummaryGroup(fillColumns);
// use an empty last page footer so that the regular page footer doesn't
// show on the last page
this.lastPageFooter = new JRDesignBand();
this.lastPageFooter.setHeight(0);
}
else
{
// use the regular page footer
this.lastPageFooter = null;
}
this.noData = createNoData(table.getNoData());
}
public String getTableName()
{
return tableName;
}
protected class ReportBandInfo
{
final JRDesignBand band;
final String bandId;
final List rows = new ArrayList();
ReportBandInfo(JRDesignBand band, String bandId)
{
this.band = band;
this.bandId = bandId;
}
protected void cellAdded(int rowLevel, CellInfo cell)
{
JRElement element = cell.getElement();
if (band.getHeight() < element.getHeight() + element.getY())
{
band.setHeight(element.getHeight() + element.getY());
}
BandRowInfo row = getBandRowInfo(rowLevel);
row.addCell(cell);
}
JRDesignElementGroup getRowElementGroup(int rowLevel)
{
BandRowInfo row = getBandRowInfo(rowLevel);
JRDesignElementGroup elementGroup = row.getElementGroup();
if (elementGroup == null)
{
elementGroup = new JRDesignElementGroup();
row.setElementGroup(elementGroup);
band.addElementGroup(elementGroup);
}
return elementGroup;
}
BandRowInfo getBandRowInfo(int rowLevel)
{
int rowCount = rows.size();
if (rowLevel >= rowCount)
{
for (int level = rowCount; level <= rowLevel; ++level)
{
rows.add(new BandRowInfo());
}
}
return rows.get(rowLevel);
}
List getRows()
{
return rows;
}
String getBandId()
{
return bandId;
}
}
protected class BandRowInfo
{
JRDesignElementGroup elementGroup;
List cells = new ArrayList();
BandRowInfo()
{
}
protected JRDesignElementGroup getElementGroup()
{
return elementGroup;
}
protected void setElementGroup(JRDesignElementGroup elementGroup)
{
this.elementGroup = elementGroup;
}
protected List getCells()
{
return cells;
}
protected void addCell(CellInfo cell)
{
cells.add(cell);
}
}
protected class CellInfo
{
final JRElement element;
final int rowSpan;
final int colSpan;
CellInfo(JRElement element, int rowSpan, int colSpan)
{
this.element = element;
this.rowSpan = rowSpan;
this.colSpan = colSpan;
}
protected JRElement getElement()
{
return element;
}
protected int getRowSpan()
{
return rowSpan;
}
protected int getColSpan()
{
return colSpan;
}
}
protected abstract class ReportBandCreator implements ColumnVisitor
{
final ReportBandInfo bandInfo;
final FillColumn fillColumn;
int xOffset;
int yOffset;
int level;
public ReportBandCreator(ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level)
{
this.bandInfo = bandInfo;
this.fillColumn = fillColumn;
this.xOffset = xOffset;
this.yOffset = yOffset;
this.level = level;
}
protected boolean isEmpty(Cell cell)
{
return cell == null;
}
@Override
public Void visitColumn(Column column)
{
Cell cell = columnCell(column);
if (!isEmpty(cell))
{
int rowSpan = cell.getRowSpan() == null ? 1 : cell.getRowSpan();
int rowLevel = level + rowSpan - 1;
JRDesignElementGroup elementGroup = bandInfo.getRowElementGroup(rowLevel);
JRElement cellElement = createColumnCell(column, elementGroup, cell);
elementGroup.addElement(cellElement);
bandInfo.cellAdded(level, new CellInfo(cellElement, rowSpan, fillColumn.getColSpan()));
yOffset += cell.getHeight();
}
xOffset += column.getWidth();
return null;
}
protected UUID deriveUUID()
{
UUID columnUUID = fillColumn.getTableColumn().getUUID();
UUID derived = DigestUtils.instance().deriveUUID(columnUUID, bandInfo.getBandId());
return derived;
}
protected abstract Cell columnCell(Column column);
protected JRElement createColumnCell(Column column, JRElementGroup parentGroup, Cell cell)
{
return createColumnCell(column, parentGroup, cell, false);
}
protected JRElement createColumnCell(Column column, JRElementGroup parentGroup, Cell cell, boolean forceFrame)
{
return createCell(parentGroup, cell,
column.getWidth(), fillColumn.getWidth(),
xOffset, yOffset, column.hashCode(), deriveUUID(), forceFrame);
}
@Override
public Void visitColumnGroup(ColumnGroup columnGroup)
{
Cell cell = columnGroupCell(columnGroup);
int cellHeight = 0;
int sublevel = level;
if (cell != null)
{
int rowSpan = cell.getRowSpan() == null ? 1 : cell.getRowSpan();
int rowLevel = level + rowSpan - 1;
int colSpan = 0;
for (FillColumn subcolumn : fillColumn.getSubcolumns())
{
colSpan += subcolumn.getColSpan();
}
colSpan = colSpan == 0 ? 1 : colSpan;
JRDesignElementGroup elementGroup = bandInfo.getRowElementGroup(rowLevel);
JRElement cellElement = createColumnGroupCell(columnGroup, cell, elementGroup);
elementGroup.addElement(cellElement);
bandInfo.cellAdded(level, new CellInfo(cellElement, rowSpan, colSpan));
cellHeight = cell.getHeight();
sublevel += rowSpan;
}
for (FillColumn subcolumn : fillColumn.getSubcolumns())
{
ReportBandCreator subVisitor = createSubVisitor(subcolumn,
xOffset, yOffset + cellHeight, sublevel);
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
return null;
}
protected JRElement createColumnGroupCell(ColumnGroup columnGroup, Cell cell, JRDesignElementGroup elementGroup)
{
return createCell(elementGroup, cell,
columnGroup.getWidth(), fillColumn.getWidth(),
xOffset, yOffset, null, deriveUUID(), false);
}
protected abstract Cell columnGroupCell(ColumnGroup group);
protected abstract ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int subLevel);
public void visit()
{
fillColumn.getTableColumn().visitColumn(this);
}
}
protected abstract class ReverseReportBandCreator extends ReportBandCreator
{
public ReverseReportBandCreator(ReportBandInfo bandInfo,
FillColumn fillColumn, int xOffset, int yOffset, int level)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
}
@Override
public Void visitColumnGroup(ColumnGroup columnGroup)
{
Cell cell = columnGroupCell(columnGroup);
int rowSpan;
if (cell == null)
{
rowSpan = 0;
}
else if (cell.getRowSpan() == null)
{
rowSpan = 1;
}
else
{
rowSpan = cell.getRowSpan();
}
int origXOffset = xOffset;
int origYOffset = yOffset;
int colSpan = 0;
for (FillColumn subcolumn : fillColumn.getSubcolumns())
{
ReportBandCreator subVisitor = createSubVisitor(subcolumn,
xOffset, origYOffset, level + rowSpan);
subVisitor.visit();
xOffset = subVisitor.xOffset;
if (subVisitor.yOffset > yOffset)
{
yOffset = subVisitor.yOffset;
}
colSpan += subcolumn.getColSpan();
}
colSpan = colSpan == 0 ? 1 : colSpan;
if (cell != null)
{
int rowLevel = level + rowSpan - 1;
JRDesignElementGroup elementGroup = bandInfo.getRowElementGroup(rowLevel);
JRElement cellElement = createCell(elementGroup, cell,
columnGroup.getWidth(), fillColumn.getWidth(),
origXOffset, yOffset, null, deriveUUID(), false);
elementGroup.addElement(cellElement);
bandInfo.cellAdded(level, new CellInfo(cellElement, rowSpan, colSpan));
yOffset += cell.getHeight();
}
return null;
}
}
protected class DetailBandCreator extends ReportBandCreator
{
public DetailBandCreator(ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
}
@Override
protected Cell columnCell(Column column)
{
return column.getDetailCell();
}
@Override
protected Cell columnGroupCell(ColumnGroup group)
{
return null;
}
@Override
protected ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int sublevel)
{
return new DetailBandCreator(bandInfo, subcolumn, xOffset, yOffset, sublevel);
}
@Override
protected boolean isEmpty(Cell cell)
{
if (super.isEmpty(cell))
{
return true;
}
// also threat zero height cells as empty.
// FIXME only doing this for the detail cells to minimize impact, it should apply to all cells
List children = cell.getChildren();
return cell.getHeight() == 0 && (children == null || children.isEmpty());
}
}
protected JRBand createDetailBand(List fillColumns)
{
final JRDesignBand detailBand = new JRDesignBand();
detailBand.setSplitType(SplitTypeEnum.PREVENT);
detailBand.setPrintWhenExpression(table.getDetail() == null ? null : table.getDetail().getPrintWhenExpression());
ReportBandInfo bandInfo = new ReportBandInfo(detailBand, BandTypeEnum.DETAIL.getName());
int xOffset = 0;
for (FillColumn subcolumn : fillColumns)
{
DetailBandCreator subVisitor = new DetailBandCreator(
bandInfo, subcolumn, xOffset, 0, 0);
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
setPdfTags(bandInfo, false);
return detailBand;
}
protected class ColumnHeaderCreator extends ReportBandCreator
{
private Map headerBaseProperties;
private final AtomicBoolean firstColumn;// we need a mutable boolean reference
public ColumnHeaderCreator(ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level,
Map headerBaseProperties, AtomicBoolean firstColumn)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
this.headerBaseProperties = headerBaseProperties;
this.firstColumn = firstColumn;
}
@Override
protected Cell columnCell(Column column)
{
return column.getColumnHeader();
}
@Override
protected JRDesignFrame createColumnCell(Column column, JRElementGroup parentGroup, Cell cell)
{
JRDesignFrame frame = (JRDesignFrame) createColumnCell(column, parentGroup, cell, true);
addHeaderToolbarElement(column, frame, TableUtil.getCellElement(JRTextField.class, column.getDetailCell(), true));
return frame;
}
protected JRExpression getColumnHeaderLabelExpression(Cell header)
{
List detailElements = header == null ? null : header.getChildren();
// only consider cells with a single text fields
if (detailElements == null || detailElements.size() != 1)
{
return null;
}
JRChild detailElement = detailElements.get(0);
if (detailElement instanceof JRTextField)
{
return ((JRTextField) detailElement).getExpression();
}
if (detailElement instanceof JRStaticText)
{
return builtinEvaluatorFactory.createConstantExpression(((JRStaticText)detailElement).getText());
}
return null;
}
protected void addHeaderToolbarElement(Column column, JRDesignFrame frame, JRTextField sortTextField)
{
int columnIndex = TableUtil.getColumnIndex(column, table);
Pair columnData = columnInteractivityMapping.get(column);
boolean interactiveColumn = columnData.first();
if (sortTextField != null && interactiveColumn)
{
Cell header = column.getColumnHeader();
JRDesignGenericElement genericElement = new JRDesignGenericElement(header.getDefaultStyleProvider());
genericElement.setGenericType(HeaderToolbarElement.ELEMENT_TYPE);
genericElement.setPositionType(net.sf.jasperreports.engine.type.PositionTypeEnum.FIX_RELATIVE_TO_TOP);
genericElement.setX(0);
genericElement.setY(0);
// TODO lucianc setting to 1 for now; we can't set to frame size as we might not know the padding
genericElement.setHeight(1);
genericElement.setWidth(1);
genericElement.setMode(ModeEnum.TRANSPARENT);
//genericElement.setStretchType(StretchTypeEnum.RELATIVE_TO_BAND_HEIGHT);
String fieldOrVariableName = null;
SortFieldTypeEnum columnType = null;
FilterTypesEnum filterType = null;
TimeZone formatTimeZone = null;//FIXME also define via properties when sortTextField is not single chunk
String suffix = "";
boolean hasFieldOrVariable = false;
if (column.getPropertiesMap().containsProperty(PROPERTY_COLUMN_FIELD))
{
hasFieldOrVariable = true;
fieldOrVariableName = column.getPropertiesMap().getProperty(PROPERTY_COLUMN_FIELD);
columnType = SortFieldTypeEnum.FIELD;
JRField field = getField(fieldOrVariableName);
if (field != null)
{
filterType = HeaderToolbarElementUtils.getFilterType(field.getValueClass());
} else
{
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_FIELD_NOT_FOUND,
new Object[]{fieldOrVariableName}
);
}
} else if (column.getPropertiesMap().containsProperty(PROPERTY_COLUMN_VARIABLE))
{
hasFieldOrVariable = true;
fieldOrVariableName = column.getPropertiesMap().getProperty(PROPERTY_COLUMN_VARIABLE);
columnType = SortFieldTypeEnum.VARIABLE;
JRVariable variable = getVariable(fieldOrVariableName);
if (variable != null)
{
filterType = HeaderToolbarElementUtils.getFilterType(variable.getValueClass());
} else
{
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_VARIABLE_NOT_FOUND,
new Object[]{fieldOrVariableName}
);
}
} else if (TableUtil.hasSingleChunkExpression(sortTextField))
{
JRExpressionChunk sortExpression = sortTextField.getExpression().getChunks()[0];
fieldOrVariableName = sortExpression.getText();
switch (sortExpression.getType())
{
case JRExpressionChunk.TYPE_FIELD:
columnType = SortFieldTypeEnum.FIELD;
JRField field = getField(fieldOrVariableName);
filterType = HeaderToolbarElementUtils.getFilterType(field.getValueClass());
formatTimeZone = getFormatTimeZone(sortTextField, field.getValueClass());
break;
case JRExpressionChunk.TYPE_VARIABLE:
columnType = SortFieldTypeEnum.VARIABLE;
JRVariable variable = getVariable(fieldOrVariableName);
filterType = HeaderToolbarElementUtils.getFilterType(variable.getValueClass());
formatTimeZone = getFormatTimeZone(sortTextField, variable.getValueClass());
break;
default:
// never
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_UNRECOGNIZED_FILTER_EXPRESSION_TYPE,
new Object[]{sortExpression.getType()}
);
}
}
boolean isSortable = propertiesUtil.getBooleanProperty(column.getPropertiesMap(), PROPERTY_COLUMN_SORTABLE, true) && fieldOrVariableName != null;
boolean isFilterable = propertiesUtil.getBooleanProperty(column.getPropertiesMap(), PROPERTY_COLUMN_FILTERABLE, true)
&& fieldOrVariableName != null && TableUtil.isFilterable(sortTextField)
&& filterType != null;
if (isSortable)
{ // column is sortable
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_CAN_SORT, Boolean.TRUE.toString());
//FIXMEJIVE consider moving in separate method
JRSortField[] sortFields = TableReport.this.mainDataset.getSortFields();
if (sortFields != null)
{
for(JRSortField sortField : sortFields)
{
if (
sortField.getName().equals(fieldOrVariableName)
&& sortField.getType() == columnType
)
{
suffix +=
""
+ (sortField.getOrderValue() == SortOrderEnum.ASCENDING
? propertiesUtil.getProperty(PROPERTY_UP_ARROW_CHAR)
: (sortField.getOrderValue() == SortOrderEnum.DESCENDING
? propertiesUtil.getProperty(PROPERTY_DOWN_ARROW_CHAR)
: ""));
}
}
}
} else
{ // column is not sortable
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_CAN_SORT, Boolean.FALSE.toString());
}
if (isFilterable)
{ // column is filterable
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_CAN_FILTER, Boolean.TRUE.toString());
JasperReportsContext jasperReportsContext = fillContext.getFiller().getJasperReportsContext();
String serializedFilters = TableReport.this.mainDataset.getPropertiesMap().getProperty(FilterCommand.DATASET_FILTER_PROPERTY);
if (serializedFilters != null)
{
List existingFilters = JacksonUtil.getInstance(jasperReportsContext).loadList(serializedFilters, FieldFilter.class);
if (existingFilters != null)
{
List fieldFilters = new ArrayList();
SortElementHtmlHandler.getFieldFilters(new CompositeDatasetFilter(existingFilters), fieldFilters, fieldOrVariableName);
if (fieldFilters.size() > 0)
{
suffix += "" + propertiesUtil.getProperty(PROPERTY_FILTER_CHAR);
}
}
}
if (formatTimeZone != null && !formatTimeZone.equals(fillContext.getFiller().getFillContext().getMasterTimeZone()))
{
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_FORMAT_TIME_ZONE,
JRDataUtils.getTimeZoneId(formatTimeZone));
}
} else
{ // column is not filterable
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_CAN_FILTER, Boolean.FALSE.toString());
}
if (suffix.length() > 0)
{
addIconLabelComponent(column, frame, suffix);
// HeaderLabelBuiltinExpression evaluator = HeaderLabelUtil.alterHeaderLabel(frame, suffix);
// if (evaluator != null)
// {
// builtinEvaluators.put(evaluator.getExpression(), evaluator);
// }
}
if (isSortable || isFilterable) {
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_TYPE, columnType.getName());
}
if (filterType != null)
{
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_FILTER_TYPE, filterType.getName());
}
if (columnType != null && hasFieldOrVariable) {
String property = SortFieldTypeEnum.FIELD.equals(columnType) ? HeaderToolbarElement.PROPERTY_COLUMN_FIELD : HeaderToolbarElement.PROPERTY_COLUMN_VARIABLE;
genericElement.getPropertiesMap().setProperty(property, fieldOrVariableName);
}
String columnName = fieldOrVariableName != null ? fieldOrVariableName : String.valueOf(columnIndex);
String columnUuid = column.getUUID().toString();//columnName + "_" + column.hashCode();
String cellId = columnName + "_" + column.hashCode();
if (firstColumn.compareAndSet(false, true)) {
// only setting on the first column to save memory
//FIXME a cleaner approach would be to set these another single generic element
addColumnLabelParameters(genericElement, table);
// setting component name on first column
String tableName = propertiesUtil.getProperty(JRComponentElement.PROPERTY_COMPONENT_NAME, fillContext.getComponentElement());
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_TABLE_NAME, tableName);
// set the hasFloatingHeader property on first column
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_FLOATING_HEADER, String.valueOf(hasFloatingHeader));
}
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_UUID, columnUuid);
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_INDEX, Integer.toString(columnIndex));
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_NAME, columnName);
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_COMPONENT_NAME, columnData.second());
genericElement.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_TABLE_UUID, fillContext.getComponentElement().getUUID().toString());
addElementParameter(genericElement, HeaderToolbarElement.PARAMETER_COLUMN_LABEL, getColumnHeaderLabelExpression(header));
frame.getPropertiesMap().setProperty(HtmlExporter.PROPERTY_HTML_CLASS, "jrcolHeader" + (interactiveColumn ? " interactiveElement" : ""));
frame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_UUID, columnUuid);
frame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_TABLE_UUID, fillContext.getComponentElement().getUUID().toString());
frame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_INDEX, String.valueOf(columnIndex));
String cellIdFixedPart = JRStringUtil.getCSSClass(cellId + "_");
TableIndexProperties cellIdProperties = new TableIndexProperties(
HeaderToolbarElement.PROPERTY_CELL_ID, cellIdFixedPart);
tableIndexProperties.add(cellIdProperties);
assert frame.getPropertiesMap().getBaseProperties() == null;
frame.getPropertiesMap().setBaseProperties(cellIdProperties.getPropertiesMap());
String classFixedPart = TableReport.HTML_CLASS_CELL + " " + TableReport.HTML_CLASS_CELL_PREFIX + cellIdFixedPart;
TableIndexProperties columnClassProperties = new TableIndexProperties(
HtmlExporter.PROPERTY_HTML_CLASS, classFixedPart);
tableIndexProperties.add(columnClassProperties);
headerBaseProperties.put(column.hashCode(), columnClassProperties.getPropertiesMap());
frame.addElement(0, genericElement);
} else if (isInteractiveTable)
{
String columnName = String.valueOf(columnIndex);
String cellId = columnName + "_" + column.hashCode();
frame.getPropertiesMap().setProperty(HtmlExporter.PROPERTY_HTML_CLASS, "jrcolHeader");
frame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_UUID, column.getUUID().toString());
frame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_CELL_ID, cellId);
frame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_TABLE_UUID, fillContext.getComponentElement().getUUID().toString());
frame.getPropertiesMap().setProperty(HeaderToolbarElement.PROPERTY_COLUMN_INDEX, String.valueOf(columnIndex));
}
}
protected void addIconLabelComponent(Column column, JRDesignFrame frame, String suffix)
{
List children = frame.getChildren();
if (children.size() > 0)
{
JRBaseTextElement headerTextElement = (JRBaseTextElement)children.get(0);
if (headerTextElement != null)
{
JRComponentElement componentElement =
IconLabelComponentUtil.getInstance(fillContext.getFiller().getJasperReportsContext()).createIconLabelComponentElement(headerTextElement);
IconLabelComponent iconLabelComponent = (IconLabelComponent)componentElement.getComponent();
JRDesignTextField labelTextField = (JRDesignTextField)iconLabelComponent.getLabelTextField();
if (headerTextElement instanceof JRTextField)
{
labelTextField.setExpression(((JRTextField) headerTextElement).getExpression());
}
else if (headerTextElement instanceof JRStaticText)
{
labelTextField.setExpression(builtinEvaluatorFactory.createConstantExpression(((JRStaticText)headerTextElement).getText()));
}
JRDesignTextField iconTextField = (JRDesignTextField)iconLabelComponent.getIconTextField();
iconTextField.setExpression(builtinEvaluatorFactory.createConstantExpression(suffix));
componentElement.getPropertiesMap().setProperty(MatcherExporterFilter.PROPERTY_MATCHER_EXPORT_FILTER_KEY, TABLE_HEADER_ICON_LABEL_MATCHER_EXPORT_KEY);
JRBaseElement element = (JRBaseElement)frame.getChildren().get(0);
element.getPropertiesMap().setProperty(MatcherExporterFilter.PROPERTY_MATCHER_EXPORT_FILTER_KEY, TABLE_HEADER_LABEL_MATCHER_EXPORT_KEY);
//frame.getChildren().remove(0);
frame.getChildren().add(componentElement);
}
}
}
protected void addElementParameter(JRDesignGenericElement element, String name, Object value)
{
JRDesignGenericElementParameter param = new JRDesignGenericElementParameter();
param.setName(name);
JRDesignExpression valueExpression = builtinEvaluatorFactory.createConstantExpression(value);
param.setValueExpression(valueExpression);
element.addParameter(param);
}
protected void addElementParameter(JRDesignGenericElement element, String name, JRExpression expression)
{
JRDesignGenericElementParameter param = new JRDesignGenericElementParameter();
param.setName(name);
param.setValueExpression(expression);
element.addParameter(param);
}
protected void addColumnLabelParameters(JRDesignGenericElement element, TableComponent table) {
List columns = TableUtil.getAllColumns(table);
for(int i = 0, ln = columns.size(); i < ln; i++) {
BaseColumn column = columns.get(i);
JRExpression columnHeaderExpression = getColumnHeaderLabelExpression(column.getColumnHeader());
boolean interactiveColumn = columnInteractivityMapping.get(column).first() && (TableUtil.getCellElement(JRTextField.class, ((Column)column).getDetailCell(), true) != null);
String paramName = HeaderToolbarElement.PARAM_COLUMN_LABEL_PREFIX + i + "|" + column.getUUID().toString() + "|" + interactiveColumn;
addElementParameter(element, paramName, columnHeaderExpression);
}
}
@Override
protected Cell columnGroupCell(ColumnGroup group)
{
return group.getColumnHeader();
}
@Override
protected ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int sublevel)
{
return new ColumnHeaderCreator(bandInfo, subcolumn, xOffset, yOffset, sublevel,
headerHtmlBaseProperties, firstColumn);
}
@Override
protected JRElement createColumnGroupCell(ColumnGroup columnGroup, Cell cell, JRDesignElementGroup elementGroup)
{
JRDesignFrame frame = (JRDesignFrame) createCell(elementGroup, cell,
columnGroup.getWidth(), fillColumn.getWidth(),
xOffset, yOffset, null, deriveUUID(), true);
frame.getPropertiesMap().setProperty(HtmlExporter.PROPERTY_HTML_CLASS, "jrcolGroupHeader");
return frame;
}
}
protected TimeZone getFormatTimeZone(JRTextField textField, Class valueClass)
{
//FIXME this duplicates the logic in JRFillTextField.determineOwnTimeZone()
String formatTimeZone = null;
if (textField.hasProperties())
{
formatTimeZone = textField.getPropertiesMap().getProperty(JRTextField.PROPERTY_FORMAT_TIMEZONE);
}
if (formatTimeZone == null || formatTimeZone.isEmpty())
{
if (java.sql.Date.class.isAssignableFrom(valueClass))
{
formatTimeZone = propertiesUtil.getProperty(parentReport, JRTextField.PROPERTY_SQL_DATE_FORMAT_TIMEZONE);
}
else if (java.sql.Timestamp.class.isAssignableFrom(valueClass))
{
formatTimeZone = propertiesUtil.getProperty(parentReport, JRTextField.PROPERTY_SQL_TIMESTAMP_FORMAT_TIMEZONE);
}
else if (java.sql.Time.class.isAssignableFrom(valueClass))
{
formatTimeZone = propertiesUtil.getProperty(parentReport, JRTextField.PROPERTY_SQL_TIME_FORMAT_TIMEZONE);
}
}
if (formatTimeZone == null || formatTimeZone.isEmpty())
{
formatTimeZone = propertiesUtil.getProperty(parentReport, JRTextField.PROPERTY_FORMAT_TIMEZONE);
}
TimeZone reportTimeZone = fillContext.getFillDataset().getTimeZone();
return JRDataUtils.resolveFormatTimeZone(formatTimeZone, reportTimeZone);
}
protected JRDesignBand createColumnHeader(List fillColumns)
{
JRDesignBand columnHeader = new JRDesignBand();
columnHeader.setSplitType(SplitTypeEnum.PREVENT);
columnHeader.setPrintWhenExpression(table.getColumnHeader() == null ? null : table.getColumnHeader().getPrintWhenExpression());
ReportBandInfo bandInfo = new ReportBandInfo(columnHeader, BandTypeEnum.COLUMN_HEADER.getName());
int xOffset = 0;
for (FillColumn subcolumn : fillColumns)
{
ColumnHeaderCreator subVisitor = new ColumnHeaderCreator(
bandInfo, subcolumn, xOffset, 0, 0, headerHtmlBaseProperties,
new AtomicBoolean());
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
setPdfTags(bandInfo, true);
if (columnHeader.getHeight() == 0)
{
columnHeader = null;
}
return columnHeader;
}
protected class PageFooterCreator extends ReverseReportBandCreator
{
public PageFooterCreator(ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
}
@Override
protected Cell columnCell(Column column)
{
return column.getColumnFooter();
}
@Override
protected Cell columnGroupCell(ColumnGroup group)
{
return group.getColumnFooter();
}
@Override
protected ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int sublevel)
{
return new PageFooterCreator(bandInfo, subcolumn, xOffset, yOffset, sublevel);
}
}
protected JRDesignBand createPageFooter(List fillColumns)
{
JRDesignBand pageFooter = new JRDesignBand();
pageFooter.setSplitType(SplitTypeEnum.PREVENT);
pageFooter.setPrintWhenExpression(table.getColumnFooter() == null ? null : table.getColumnFooter().getPrintWhenExpression());
ReportBandInfo bandInfo = new ReportBandInfo(pageFooter, BandTypeEnum.PAGE_FOOTER.getName());
int xOffset = 0;
for (FillColumn subcolumn : fillColumns)
{
PageFooterCreator subVisitor = new PageFooterCreator(
bandInfo, subcolumn, xOffset, 0, 0);
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
setPdfTags(bandInfo, false);
if (pageFooter.getHeight() == 0)
{
pageFooter = null;
}
return pageFooter;
}
protected JRDesignBand createNoData(BaseCell cell)
{
JRDesignBand noData = null;
if (cell != null)
{
noData = new JRDesignBand();
noData.setSplitType(SplitTypeEnum.PREVENT);
//noData.setPrintWhenExpression(table.getColumnFooter() == null ? null : table.getColumnFooter().getPrintWhenExpression());
noData.addElement(
createCell(
null,
cell,
fillContext.getComponentElement().getWidth(),
fillContext.getComponentElement().getWidth(),
0,
0,
null,
null,
false
)
);
// if (noData.getHeight() == 0)
// {
// noData = null;
// }
}
return noData;
}
protected class TitleCreator extends ReportBandCreator
{
public TitleCreator(ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
}
@Override
protected Cell columnCell(Column column)
{
return column.getTableHeader();
}
@Override
protected Cell columnGroupCell(ColumnGroup group)
{
return group.getTableHeader();
}
@Override
protected ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int sublevel)
{
return new TitleCreator(bandInfo, subcolumn, xOffset, yOffset, sublevel);
}
}
protected JRDesignBand createTitle(List fillColumns)
{
JRDesignBand title = new JRDesignBand();
title.setSplitType(SplitTypeEnum.PREVENT);
title.setPrintWhenExpression(table.getTableHeader() == null ? null : table.getTableHeader().getPrintWhenExpression());
ReportBandInfo bandInfo = new ReportBandInfo(title, BandTypeEnum.TITLE.getName());
int xOffset = 0;
for (FillColumn subcolumn : fillColumns)
{
TitleCreator subVisitor = new TitleCreator(
bandInfo, subcolumn, xOffset, 0, 0);
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
setPdfTags(bandInfo, false);
if (title.getHeight() == 0) //FIXMETABLE not sure we actually need this; maybe check the section is truly empty; do the same for the other sections as well
{
title = null;
}
return title;
}
protected class SummaryCreator extends ReverseReportBandCreator
{
public SummaryCreator(ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
}
@Override
protected Cell columnCell(Column column)
{
return column.getTableFooter();
}
@Override
protected Cell columnGroupCell(ColumnGroup group)
{
return group.getTableFooter();
}
@Override
protected ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int sublevel)
{
return new SummaryCreator(bandInfo, subcolumn, xOffset, yOffset, sublevel);
}
}
protected JRDesignBand createSummary(List fillColumns)
{
JRDesignBand summary = new JRDesignBand();
summary.setSplitType(SplitTypeEnum.PREVENT);
summary.setPrintWhenExpression(table.getTableFooter() == null ? null : table.getTableFooter().getPrintWhenExpression());
ReportBandInfo bandInfo = new ReportBandInfo(summary, BandTypeEnum.SUMMARY.getName());
int xOffset = 0;
for (FillColumn subcolumn : fillColumns)
{
SummaryCreator subVisitor = new SummaryCreator(
bandInfo, subcolumn, xOffset, 0, 0);
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
setPdfTags(bandInfo, false);
if (summary.getHeight() == 0)
{
summary = null;
}
return summary;
}
protected class GroupHeaderCreator extends ReportBandCreator
{
private final String groupName;
public GroupHeaderCreator(String groupName,
ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
this.groupName = groupName;
}
@Override
protected Cell columnCell(Column column)
{
return column.getGroupHeader(groupName);
}
@Override
protected Cell columnGroupCell(ColumnGroup group)
{
return group.getGroupHeader(groupName);
}
@Override
protected ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int sublevel)
{
return new GroupHeaderCreator(groupName,
bandInfo, subcolumn, xOffset, yOffset, sublevel);
}
}
protected JRBand createGroupHeader(String groupName, List fillColumns)
{
JRDesignBand header = new JRDesignBand();
header.setSplitType(SplitTypeEnum.PREVENT);
header.setPrintWhenExpression(table.getGroupHeader(groupName) == null ? null : table.getGroupHeader(groupName).getPrintWhenExpression());
ReportBandInfo bandInfo = new ReportBandInfo(header, BandTypeEnum.GROUP_HEADER + "-" + groupName);
int xOffset = 0;
for (FillColumn subcolumn : fillColumns)
{
GroupHeaderCreator subVisitor = new GroupHeaderCreator(groupName,
bandInfo, subcolumn, xOffset, 0, 0);
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
setPdfTags(bandInfo, false);
if (header.getHeight() == 0)
{
header = null;
}
return header;
}
protected class GroupFooterCreator extends ReverseReportBandCreator
{
private final String groupName;
public GroupFooterCreator(String groupName,
ReportBandInfo bandInfo, FillColumn fillColumn,
int xOffset, int yOffset, int level)
{
super(bandInfo, fillColumn, xOffset, yOffset, level);
this.groupName = groupName;
}
@Override
protected Cell columnCell(Column column)
{
return column.getGroupFooter(groupName);
}
@Override
protected Cell columnGroupCell(ColumnGroup group)
{
return group.getGroupFooter(groupName);
}
@Override
protected ReportBandCreator createSubVisitor(FillColumn subcolumn,
int xOffset, int yOffset, int sublevel)
{
return new GroupFooterCreator(groupName,
bandInfo, subcolumn, xOffset, yOffset, sublevel);
}
}
protected JRBand createGroupFooter(String groupName, List fillColumns)
{
JRDesignBand footer = new JRDesignBand();
footer.setSplitType(SplitTypeEnum.PREVENT);
footer.setPrintWhenExpression(table.getGroupFooter(groupName) == null ? null : table.getGroupFooter(groupName).getPrintWhenExpression());
ReportBandInfo bandInfo = new ReportBandInfo(footer, BandTypeEnum.GROUP_FOOTER + "-" + groupName);
int xOffset = 0;
for (FillColumn subcolumn : fillColumns)
{
GroupFooterCreator subVisitor = new GroupFooterCreator(groupName,
bandInfo, subcolumn, xOffset, 0, 0);
subVisitor.visit();
xOffset = subVisitor.xOffset;
}
setPdfTags(bandInfo, false);
if (footer.getHeight() == 0)//FIXMENOW why zero height cells are not generating any content? could be expanding frames...
{
footer = null;
}
return footer;
}
private void setGroupBands(List fillColumns)
{
TableReportGroup[] groups = mainDataset.getTableGroups();
if (groups != null)
{
for (TableReportGroup group : groups)
{
JRBand header = createGroupHeader(group.getName(), fillColumns);
if (header != null)
{
group.setGroupHeader(header);
}
JRBand footer = createGroupFooter(group.getName(), fillColumns);
if (footer != null)
{
group.setGroupFooter(footer);
}
}
}
}
private void setPdfTags(ReportBandInfo bandInfo, boolean isHeader)
{
if (!isGeneratePdfTags)
{
return;
}
String cellTagProp = isHeader ? JRPdfExporterTagHelper.PROPERTY_TAG_TH : JRPdfExporterTagHelper.PROPERTY_TAG_TD;
List rows = bandInfo.getRows();
for (BandRowInfo row : rows)
{
List cells = row.getCells();
CellInfo firstCell = cells.get(0);
firstCell.getElement().getPropertiesMap().setProperty(JRPdfExporterTagHelper.PROPERTY_TAG_TR, JRPdfExporterTagHelper.TAG_START);
for (CellInfo cell : cells)
{
cell.getElement().getPropertiesMap().setProperty(cellTagProp, JRPdfExporterTagHelper.TAG_FULL);
if (cell.getRowSpan() > 1)
{
cell.getElement().getPropertiesMap().setProperty(JRPdfExporterTagHelper.PROPERTY_TAG_ROWSPAN, String.valueOf(cell.getRowSpan()));
}
if (cell.getColSpan() > 1)
{
cell.getElement().getPropertiesMap().setProperty(JRPdfExporterTagHelper.PROPERTY_TAG_COLSPAN, String.valueOf(cell.getColSpan()));
}
}
CellInfo lastCell = cells.get(cells.size() - 1);
lastCell.getElement().getPropertiesMap().setProperty(
JRPdfExporterTagHelper.PROPERTY_TAG_TR,
cells.size() == 1 ? JRPdfExporterTagHelper.TAG_FULL : JRPdfExporterTagHelper.TAG_END
);
}
}
protected boolean isGeneratePdfTags()
{
return isGeneratePdfTags;
}
protected static final String TABLE_SCRIPTLET_NAME = "__Table";
protected class SummaryGroupFooterPrintWhenEvaluator implements BuiltinExpressionEvaluator
{
private JRValueParameter tableScriptletParam;
private TableReportScriptlet tableScriptlet;
@Override
public void init(Map parametersMap,
Map fieldsMap,
Map variablesMap,
WhenResourceMissingTypeEnum resourceMissingType)
throws JRException
{
tableScriptletParam = parametersMap.get(TABLE_SCRIPTLET_NAME
+ JRScriptlet.SCRIPTLET_PARAMETER_NAME_SUFFIX);
}
protected void ensureValue()
{
if (tableScriptlet == null)
{
tableScriptlet = (TableReportScriptlet) tableScriptletParam.getValue();
}
}
@Override
public Object evaluate(DatasetExpressionEvaluator evaluator) throws JRExpressionEvalException
{
ensureValue();
return tableScriptlet.hasDetailOnPage();
}
@Override
public Object evaluateEstimated(DatasetExpressionEvaluator evaluator) throws JRExpressionEvalException
{
ensureValue();
return tableScriptlet.hasDetailOnPage();
}
@Override
public Object evaluateOld(DatasetExpressionEvaluator evaluator) throws JRExpressionEvalException
{
ensureValue();
return tableScriptlet.hasDetailOnPage();
}
}
protected int computeTableWidth(List fillColumns)
{
int width = 0;
for (FillColumn column : fillColumns)
{
width += column.getWidth();
}
return width;
}
protected void addSummaryGroup(List fillColumns)
{
JRDesignGroup summaryGroup = new JRDesignGroup();
summaryGroup.setName(SUMMARY_GROUP_NAME);//TODO check for uniqueness
JRDesignBand groupFooter = new JRDesignBand();
groupFooter.setSplitType(SplitTypeEnum.PREVENT);
groupFooter.setHeight(pageFooter.getHeight());
// we need to put everything in a frame so that we can tell the frame
// not to print when there are no detail bands on the current page
//
// we can't do that directly to the band since its print when expression
// is evaluated too soon
JRDesignFrame footerFrame = new JRDesignFrame();
DigestUtils digestUtils = DigestUtils.instance();
UUID uuid = digestUtils.deriveUUID(
fillContext.getComponentElement().getUUID(),
BandTypeEnum.GROUP_FOOTER + "-" + SUMMARY_GROUP_NAME);
footerFrame.setUUID(uuid);
footerFrame.setX(0);
footerFrame.setY(0);
footerFrame.setWidth(computeTableWidth(fillColumns));
footerFrame.setHeight(pageFooter.getHeight());
footerFrame.getLineBox().setPadding((Integer)0);
footerFrame.getLineBox().getPen().setLineWidth((Float)0f);
footerFrame.setRemoveLineWhenBlank(true);
JRDesignExpression footerPrintWhen = builtinEvaluatorFactory.createExpression(new SummaryGroupFooterPrintWhenEvaluator());
footerFrame.setPrintWhenExpression(footerPrintWhen);
// clone the contents of the page footer in the frame
List footerChildren = pageFooter.getChildren();
for (Iterator iterator = footerChildren.iterator(); iterator
.hasNext();)
{
JRChild child = iterator.next();
JRChild childClone = (JRChild) child.clone(footerFrame);
if (childClone instanceof JRElement)
{
footerFrame.addElement((JRElement) childClone);
}
else if (childClone instanceof JRElementGroup)
{
footerFrame.addElementGroup((JRElementGroup) childClone);
}
else
{
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_UNKNOWN_CHILD_TYPE,
new Object[]{childClone.getClass().getName()}
);
}
}
// set derived uuid for cloned elements so that it does not get randomly regenerated;
// we count on the fact that cloned elements would be as many as the original elements
// and appear in the same order in the array
JRElement[] footerElements = pageFooter.getElements();
JRElement[] footerCloneElements = footerFrame.getElements();
for (int i = 0; i < footerElements.length; i++)
{
uuid = digestUtils.deriveUUID(
footerElements[i].getUUID(),
BandTypeEnum.GROUP_FOOTER + "-" + SUMMARY_GROUP_NAME);
((JRDesignElement)footerCloneElements[i]).setUUID(uuid);
}
groupFooter.addElement(footerFrame);
((JRDesignSection) summaryGroup.getGroupFooterSection()).addBand(groupFooter);
mainDataset.addScriptlet(TABLE_SCRIPTLET_NAME, TableReportScriptlet.class);
mainDataset.addFirstGroup(summaryGroup);
}
protected JRElement createCell(JRElementGroup parentGroup, BaseCell cell,
int originalWidth, int width,
int x, int y, Integer columnHashCode, UUID uuid,
boolean forceFrame)
{
if (!forceFrame)
{
JRElement cellElement = createCellElement(parentGroup, cell, originalWidth, width, x, y, columnHashCode);
if (cellElement != null)
{
return cellElement;
}
}
JRDesignFrame frame = new JRDesignFrame(this);
frame.setUUID(uuid);
frame.setElementGroup(parentGroup);
frame.setX(x);
frame.setY(y);
frame.setWidth(width);
frame.setHeight(cell.getHeight());
frame.setPositionType(PositionTypeEnum.FLOAT);
frame.setStretchType(StretchTypeEnum.ELEMENT_GROUP_HEIGHT);
frame.setStyle(cell.getStyle());
frame.setStyleNameReference(cell.getStyleNameReference());
frame.copyBox(cell.getLineBox());
if (columnHashCode != null && headerHtmlBaseProperties.get(columnHashCode) != null) {
JRPropertiesMap propertiesMap = frame.getPropertiesMap();
assert propertiesMap != null && propertiesMap.getBaseProperties() == null;
propertiesMap.setBaseProperties(headerHtmlBaseProperties.get(columnHashCode));
}
// not transferring cell properties to the frame/element for now
for (Iterator it = cell.getChildren().iterator(); it.hasNext();)
{
JRChild child = it.next();
if (child instanceof JRElement)
{
JRElement element = (JRElement) child;
// clone the element in order to set the frame as group
element = (JRElement) element.clone(frame);
if (width != originalWidth)
{
scaleCellElement(element, originalWidth, width);
if (element instanceof JRElementGroup)//i.e. frame
{
JRElementGroup elementGroup = (JRElementGroup) element;
for (JRElement subelement : elementGroup.getElements())
{
scaleCellElement(subelement, originalWidth, width);
}
}
}
frame.addElement(element);
}
else if (child instanceof JRElementGroup)
{
JRElementGroup elementGroup = (JRElementGroup) child;
// clone the elements in order to set the frame as group
elementGroup = (JRElementGroup) elementGroup.clone(frame);
frame.addElementGroup(elementGroup);
if (width != originalWidth)
{
for (JRElement element : elementGroup.getElements())
{
scaleCellElement(element, originalWidth, width);
}
}
}
else
{
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_UNKNOWN_CHILD_TYPE,
new Object[]{child.getClass().getName()}
);
}
}
return frame;
}
protected JRElement createCellElement(JRElementGroup elementGroup, BaseCell cell,
int originalWidth, int width,
int x, int y, Integer columnHashCode)
{
List children = cell.getChildren();
if (children.size() != 1)
{
// several children
return null;
}
JRChild child = children.get(0);
if (!(child instanceof JRStaticText || child instanceof JRTextField))
{
// only doing this for texts for now
return null;
}
JRElement element = (JRElement) child;
if (element.getX() != 0
|| element.getY() != 0
|| element.getWidth() != originalWidth
|| element.getHeight() != cell.getHeight())
{
// the element is not as large as the cell
return null;
}
ModeEnum elementMode = StyleUtil.instance().resolveElementMode(element);
if (elementMode == null || elementMode == ModeEnum.TRANSPARENT)
{
// if the element is not necessarily opaque, check that the cell is transparent
ModeEnum cellMode = StyleUtil.instance().resolveMode(cell);
if (cellMode != ModeEnum.TRANSPARENT)
{
// the cell is not necessarily transparent, we need the frame
return null;
}
}
if (StyleUtil.instance().hasBox(cell))
{
// the cell has box, we need the frame
return null;
}
JRElement cellElement = element.clone(elementGroup, y);
cellElement.setX(x);
cellElement.setWidth(width);
cellElement.setStretchType(StretchTypeEnum.ELEMENT_GROUP_HEIGHT);
if (columnHashCode != null && headerHtmlBaseProperties.get(columnHashCode) != null)
{
JRPropertiesMap propertiesMap = cellElement.getPropertiesMap();
assert propertiesMap != null && propertiesMap.getBaseProperties() == null;
propertiesMap.setBaseProperties(headerHtmlBaseProperties.get(columnHashCode));
}
if (width != originalWidth)
{
scaleCellElement(element, originalWidth, width);
}
return cellElement;
}
protected void scaleCellElement(JRElement element, Integer cellWidth,
int scaledCellWidth)
{
int scaledX = (int) Math.floor(((float) element.getX() * scaledCellWidth) / cellWidth);
element.setX(scaledX);
int scaledWidth = (int) Math.floor(((float) element.getWidth() * scaledCellWidth) / cellWidth);
element.setWidth(scaledWidth);
}
protected JRSection wrapBand(JRBand band, JROrigin origin)
{
JRDesignSection section = new JRDesignSection(origin);
section.addBand(band);
return section;
}
@Override
public JRBand getBackground()
{
return null;
}
@Override
public int getBottomMargin()
{
return 0;
}
@Override
public int getColumnCount()
{
return 1;
}
@Override
public JRBand getColumnFooter()
{
return null;
}
@Override
public JRBand getColumnHeader()
{
return columnHeader;
}
@Override
public int getColumnSpacing()
{
return 0;
}
@Override
public int getColumnWidth()
{
return fillContext.getComponentElement().getWidth();
}
@Override
public JRDataset[] getDatasets()
{
return parentReport.getDatasets();
}
@Deprecated
public JRBand getDetail()
{
// see #getDetailSection()
return null;
}
@Override
public JRSection getDetailSection()
{
return detail;
}
@Override
public JRField[] getFields()
{
return mainDataset.getFields();
}
protected JRField getField(String name)
{
JRField found = null;
for (JRField field : getFields())
{
if (name.equals(field.getName()))
{
found = field;
break;
}
}
return found;
}
@Override
public String getFormatFactoryClass()
{
return parentReport.getFormatFactoryClass();
}
@Override
public JRGroup[] getGroups()
{
return mainDataset.getGroups();
}
@Override
public String[] getImports()
{
return parentReport.getImports();
}
@Override
public String getLanguage()
{
return parentReport.getLanguage();
}
@Override
public JRBand getLastPageFooter()
{
return lastPageFooter;
}
@Override
public int getLeftMargin()
{
return 0;
}
@Override
public JRDataset getMainDataset()
{
return mainDataset;
}
@Override
public String getName()
{
return mainDataset.getName();
}
@Override
public JRBand getNoData()
{
return noData;
}
@Override
public OrientationEnum getOrientationValue()
{
return OrientationEnum.PORTRAIT;
}
@Override
public JRBand getPageFooter()
{
return pageFooter;
}
@Override
public JRBand getPageHeader()
{
return null;
}
@Override
public int getPageHeight()
{
return parentReport.getPageHeight();
}
@Override
public int getPageWidth()
{
return fillContext.getComponentElement().getWidth();
}
@Override
public JRParameter[] getParameters()
{
return mainDataset.getParameters();
}
@Override
public PrintOrderEnum getPrintOrderValue()
{
return PrintOrderEnum.VERTICAL;
}
@Override
public RunDirectionEnum getColumnDirection()
{
return RunDirectionEnum.LTR;
}
@Override
public String getProperty(String name)
{
return mainDataset.getPropertiesMap().getProperty(name);
}
@Override
public String[] getPropertyNames()
{
return mainDataset.getPropertiesMap().getPropertyNames();
}
@Override
public JRQuery getQuery()
{
return mainDataset.getQuery();
}
@Override
public String getResourceBundle()
{
return mainDataset.getResourceBundle();
}
@Override
public int getRightMargin()
{
return 0;
}
@Override
public String getScriptletClass()
{
return mainDataset.getScriptletClass();
}
@Override
public JRScriptlet[] getScriptlets()
{
return mainDataset.getScriptlets();
}
@Override
public JRSortField[] getSortFields()
{
return mainDataset.getSortFields();
}
@Override
public JRStyle[] getStyles()
{
return parentReport.getStyles();
}
@Override
public JRBand getSummary()
{
return summary;
}
@Override
public JRReportTemplate[] getTemplates()
{
// the parent report's templates are always used for the subreport
return null;
}
@Override
public JRBand getTitle()
{
return title;
}
@Override
public int getTopMargin()
{
return 0;
}
@Override
public JRVariable[] getVariables()
{
return mainDataset.getVariables();
}
protected JRVariable getVariable(String name)
{
JRVariable found = null;
for (JRVariable var : getVariables())
{
if (name.equals(var.getName()))
{
found = var;
break;
}
}
return found;
}
@Override
public WhenNoDataTypeEnum getWhenNoDataTypeValue()
{
WhenNoDataTypeTableEnum whenNoDataType = table.getWhenNoDataType();
if (whenNoDataType == null)
{
whenNoDataType =
WhenNoDataTypeTableEnum.getByName(
propertiesUtil.getProperty(
fillContext.getFillDataset(),
TableComponent.CONFIG_PROPERTY_WHEN_NO_DATA_TYPE
)
);
}
switch (whenNoDataType)
{
case ALL_SECTIONS_NO_DETAIL :
{
return WhenNoDataTypeEnum.ALL_SECTIONS_NO_DETAIL;
}
case NO_DATA_CELL :
{
return WhenNoDataTypeEnum.NO_DATA_SECTION;
}
case BLANK :
default :
{
return WhenNoDataTypeEnum.NO_PAGES;
}
}
}
@Override
public SectionTypeEnum getSectionType()
{
return SectionTypeEnum.BAND;
}
@Override
public WhenResourceMissingTypeEnum getWhenResourceMissingTypeValue()
{
return mainDataset.getWhenResourceMissingTypeValue();
}
@Override
public boolean isFloatColumnFooter()
{
return true;
}
@Override
public boolean isIgnorePagination()
{
return false;
}
@Override
public boolean isSummaryNewPage()
{
return false;
}
@Override
public boolean isSummaryWithPageHeaderAndFooter()
{
return false;
}
@Override
public boolean isTitleNewPage()
{
return false;
}
@Override
public void removeProperty(String name)
{
throw new UnsupportedOperationException();
}
@Override
public void setProperty(String name, String value)
{
throw new UnsupportedOperationException();
}
@Override
public void setWhenNoDataType(WhenNoDataTypeEnum whenNoDataType)
{
throw new UnsupportedOperationException();
}
@Override
public void setWhenResourceMissingType(
WhenResourceMissingTypeEnum whenResourceMissingType)
{
throw new UnsupportedOperationException();
}
@Override
public JRStyle getDefaultStyle()
{
return parentReport.getDefaultStyle();
}
@Override
public StyleResolver getStyleResolver()
{
return parentReport.getStyleResolver();
}
@Override
public JRPropertiesHolder getParentProperties()
{
return null;
}
@Override
public JRPropertiesMap getPropertiesMap()
{
return mainDataset.getPropertiesMap();
}
@Override
public boolean hasProperties()
{
return mainDataset.hasProperties();
}
@Override
public DatasetPropertyExpression[] getPropertyExpressions()
{
return mainDataset.getPropertyExpressions();
}
@Override
public UUID getUUID()
{
return mainDataset.getUUID();
}
public boolean isInteractiveTable() {
return isInteractiveTable;
}
public void setTableInstanceIndex(int instanceIndex)
{
for (TableIndexProperties properties : tableIndexProperties)
{
properties.setTableInstanceIndex(instanceIndex);
}
}
// creates a JRPropertiesMap instance that is used as base properties for table elements.
// on each table instantiation, a property in the base instance changes its value and the
// value propagates to the print elements created by the table.
protected static class TableIndexProperties
{
private final String propertyName;
private final String classFixedPart;
private JRPropertiesMap propertiesMap;
public TableIndexProperties(String propertyName, String classFixedPart)
{
this.propertyName = propertyName;
this.classFixedPart = classFixedPart;
this.propertiesMap = new JRPropertiesMap();
}
public JRPropertiesMap getPropertiesMap()
{
return propertiesMap;
}
public void setTableInstanceIndex(int instanceIndex)
{
propertiesMap.setProperty(propertyName, classFixedPart + instanceIndex);
}
}
}