/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.elementfactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.CrosstabCell;
import org.pentaho.reporting.engine.classic.core.CrosstabCellBody;
import org.pentaho.reporting.engine.classic.core.CrosstabColumnGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabColumnGroupBody;
import org.pentaho.reporting.engine.classic.core.CrosstabGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabOtherGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabOtherGroupBody;
import org.pentaho.reporting.engine.classic.core.CrosstabRowGroup;
import org.pentaho.reporting.engine.classic.core.CrosstabRowGroupBody;
import org.pentaho.reporting.engine.classic.core.DetailsHeader;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.GroupBody;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeDataSchemaModel;
import org.pentaho.reporting.engine.classic.core.filter.types.LabelType;
import org.pentaho.reporting.engine.classic.core.filter.types.NumberFieldType;
import org.pentaho.reporting.engine.classic.core.filter.types.TextFieldType;
import org.pentaho.reporting.engine.classic.core.function.AggregationFunction;
import org.pentaho.reporting.engine.classic.core.metadata.ElementType;
import org.pentaho.reporting.engine.classic.core.style.BandStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.wizard.AutoGeneratorUtility;
import org.pentaho.reporting.engine.classic.core.wizard.ContextAwareDataSchemaModel;
import org.pentaho.reporting.engine.classic.core.wizard.DataAttributeContext;
import org.pentaho.reporting.engine.classic.core.wizard.DataAttributes;
import org.pentaho.reporting.libraries.base.util.StringUtils;
public class CrosstabBuilder implements Cloneable
{
private ArrayList<CrosstabDimension> rows;
private ArrayList<CrosstabDimension> columns;
private ArrayList<String> others;
private ArrayList<CrosstabDetail> details;
private ContextAwareDataSchemaModel dataSchemaModel;
private String groupNamePrefix;
private Float minimumWidth;
private Float minimumHeight;
private Float maximumWidth;
private Float maximumHeight;
private Float prefWidth;
private Float prefHeight;
private Boolean allowMetaDataStyling;
private Boolean allowMetaDataAttributes;
@Deprecated
public CrosstabBuilder(final DesignTimeDataSchemaModel dataSchemaModel)
{
this((ContextAwareDataSchemaModel) dataSchemaModel);
}
public CrosstabBuilder(final ContextAwareDataSchemaModel dataSchemaModel)
{
rows = new ArrayList<CrosstabDimension>();
columns = new ArrayList<CrosstabDimension>();
others = new ArrayList<String>();
details = new ArrayList<CrosstabDetail>();
this.dataSchemaModel = dataSchemaModel;
this.groupNamePrefix = "";
this.minimumHeight = 20f;
this.maximumHeight = 20f;
this.maximumWidth = 80f;
this.minimumWidth = 80f;
}
public Float getMinimumWidth()
{
return minimumWidth;
}
public void setMinimumWidth(final Float minimumWidth)
{
this.minimumWidth = minimumWidth;
}
public Float getMinimumHeight()
{
return minimumHeight;
}
public void setMinimumHeight(final Float minimumHeight)
{
this.minimumHeight = minimumHeight;
}
public Float getMaximumWidth()
{
return maximumWidth;
}
public void setMaximumWidth(final Float maximumWidth)
{
this.maximumWidth = maximumWidth;
}
public Float getMaximumHeight()
{
return maximumHeight;
}
public void setMaximumHeight(final Float maximumHeight)
{
this.maximumHeight = maximumHeight;
}
public Float getPrefWidth()
{
return prefWidth;
}
public void setPrefWidth(final Float prefWidth)
{
this.prefWidth = prefWidth;
}
public Float getPrefHeight()
{
return prefHeight;
}
public void setPrefHeight(final Float prefHeight)
{
this.prefHeight = prefHeight;
}
public Boolean getAllowMetaDataStyling()
{
return allowMetaDataStyling;
}
public void setAllowMetaDataStyling(final Boolean allowMetaDataStyling)
{
this.allowMetaDataStyling = allowMetaDataStyling;
}
public Boolean getAllowMetaDataAttributes()
{
return allowMetaDataAttributes;
}
public void setAllowMetaDataAttributes(final Boolean allowMetaDataAttributes)
{
this.allowMetaDataAttributes = allowMetaDataAttributes;
}
public String getGroupNamePrefix()
{
return groupNamePrefix;
}
public void setGroupNamePrefix(final String groupNamePrefix)
{
this.groupNamePrefix = groupNamePrefix;
}
public void addOtherDimension(final String field)
{
others.add(field);
}
public void addRowDimension(final CrosstabDimension dimension)
{
rows.add(dimension);
}
public void addRowDimension(final String field)
{
addRowDimension(new CrosstabDimension(field, field, false, "Summary"));
}
public void addRowDimension(final String field, final boolean addSummary)
{
addRowDimension(new CrosstabDimension(field, field, addSummary, "Summary"));
}
public void addColumnDimension(final CrosstabDimension dimension)
{
columns.add(dimension);
}
public void addColumnDimension(final String field)
{
addColumnDimension(new CrosstabDimension(field, field, false, "Summary"));
}
public void addColumnDimension(final String field, final boolean addSummary)
{
addColumnDimension(new CrosstabDimension(field, field, addSummary, "Summary"));
}
public void addDetails(final CrosstabDetail detail)
{
details.add(detail);
}
public void addDetails(final String field, final Class<? extends AggregationFunction> aggregation)
{
details.add(new CrosstabDetail(field, field, aggregation));
}
public List<CrosstabDimension> getRows()
{
return Collections.unmodifiableList(rows);
}
public List<CrosstabDimension> getColumns()
{
return Collections.unmodifiableList(columns);
}
public List<String> getOthers()
{
return Collections.unmodifiableList(others);
}
public List<CrosstabDetail> getDetails()
{
return Collections.unmodifiableList(details);
}
public MasterReport createReport()
{
final MasterReport report = new MasterReport();
report.setRootGroup(create());
return report;
}
public CrosstabGroup create()
{
if (columns.size() == 0)
{
throw new IllegalStateException();
}
if (rows.size() == 0)
{
throw new IllegalStateException();
}
final CrosstabCellBody cellBody = createCellBody();
GroupBody body = createColumnGroups(cellBody);
body = createRowGroups(cellBody, body);
body = createOtherGroups(body);
return new CrosstabGroup(body);
}
protected CrosstabCellBody createCellBody()
{
final CrosstabCellBody cellBody = new CrosstabCellBody();
cellBody.addElement(createDetailsCell("details-cell", null, null));
setupDetailsHeader(cellBody.getHeader());
return cellBody;
}
private GroupBody createOtherGroups(GroupBody body)
{
for (int other = others.size() - 1; other >= 0; other -= 1)
{
final String column = others.get(other);
final CrosstabOtherGroup columnGroup = createOtherGroup(body, column);
body = new CrosstabOtherGroupBody(columnGroup);
}
return body;
}
protected CrosstabOtherGroup createOtherGroup(final GroupBody body, final String column)
{
final CrosstabOtherGroup columnGroup = new CrosstabOtherGroup(body);
columnGroup.setField(column);
columnGroup.getHeader().addElement(createFieldItem(column));
return columnGroup;
}
private GroupBody createRowGroups(final CrosstabCellBody cellBody, GroupBody body)
{
for (int row = rows.size() - 1; row >= 0; row -= 1)
{
final CrosstabDimension rowDimension = rows.get(row);
final CrosstabRowGroup rowGroup = createRowGroup(cellBody, body, rowDimension);
body = new CrosstabRowGroupBody(rowGroup);
}
return body;
}
protected CrosstabRowGroup createRowGroup(final CrosstabCellBody cellBody,
final GroupBody innerBody,
final CrosstabDimension rowDimension)
{
final CrosstabRowGroup rowGroup = new CrosstabRowGroup(innerBody);
rowGroup.setName(computeGroupName(rowDimension));
rowGroup.setField(rowDimension.getField());
rowGroup.getTitleHeader().getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
rowGroup.getTitleHeader().addElement(createLabel(rowDimension.getTitle(), rowDimension.getField()));
rowGroup.getHeader().getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
rowGroup.getHeader().addElement(createFieldItem(rowDimension.getField()));
rowGroup.getSummaryHeader().getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
rowGroup.getSummaryHeader().addElement(createLabel(rowDimension.getSummaryTitle(), rowDimension.getField()));
rowGroup.setPrintSummary(rowDimension.isPrintSummary());
createSummaryCells(cellBody, rowDimension);
return rowGroup;
}
protected void createSummaryCells(final CrosstabCellBody cellBody,
final CrosstabDimension rowDimension)
{
if (rowDimension.isPrintSummary())
{
cellBody.addElement(createDetailsCell(rowDimension.getField(), rowDimension.getField(), null));
for (int col = columns.size() - 1; col >= 0; col -= 1)
{
final CrosstabDimension column = columns.get(col);
if (column.isPrintSummary())
{
cellBody.addElement(createDetailsCell
(column.getField() + "," + rowDimension.getField(), rowDimension.getField(), column.getField()));
}
}
}
}
protected void createColumnSummaryCells(final CrosstabCellBody cellBody, final CrosstabDimension column)
{
if (column.isPrintSummary())
{
cellBody.addElement(createDetailsCell(column.getField(), null, column.getField()));
}
}
protected String computeGroupName(final CrosstabDimension rowDimension)
{
return groupNamePrefix + rowDimension.getField();
}
private GroupBody createColumnGroups(final CrosstabCellBody cellBody)
{
GroupBody body = cellBody;
for (int col = columns.size() - 1; col >= 0; col -= 1)
{
final CrosstabDimension column = columns.get(col);
final CrosstabColumnGroup columnGroup = createColumnGroup(cellBody, body, column);
body = new CrosstabColumnGroupBody(columnGroup);
}
return body;
}
protected CrosstabColumnGroup createColumnGroup(final CrosstabCellBody cellBody,
final GroupBody body,
final CrosstabDimension column)
{
final CrosstabColumnGroup columnGroup = new CrosstabColumnGroup(body);
columnGroup.setName(computeGroupName(column));
columnGroup.setField(column.getField());
columnGroup.getTitleHeader().getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
columnGroup.getTitleHeader().addElement(createLabel(column.getTitle(), column.getField()));
columnGroup.getHeader().getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
columnGroup.getHeader().addElement(createFieldItem(column.getField()));
columnGroup.getSummaryHeader().getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
columnGroup.getSummaryHeader().addElement(createLabel(column.getSummaryTitle(), column.getField()));
columnGroup.setPrintSummary(column.isPrintSummary());
createColumnSummaryCells(cellBody, column);
return columnGroup;
}
protected CrosstabCell createDetailsCell(final String name, final String rowDim, final String colDim)
{
final CrosstabCell cell = createDetailsCell();
cell.setColumnField(colDim);
cell.setName(name);
return cell;
}
protected CrosstabCell createDetailsCell()
{
final CrosstabCell cell = new CrosstabCell();
cell.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
cell.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, BandStyleKeys.LAYOUT_ROW);
for (int i = 0; i < details.size(); i += 1)
{
final CrosstabDetail crosstabDetail = details.get(i);
cell.addElement(createDetailCellContent(crosstabDetail));
}
return cell;
}
protected Element createDetailCellContent(final CrosstabDetail crosstabDetail)
{
return createFieldItem(crosstabDetail.getField(), crosstabDetail.getAggregation(), true);
}
protected void setupDetailsHeader(final DetailsHeader cell)
{
cell.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, -100f);
cell.getStyle().setStyleProperty(BandStyleKeys.LAYOUT, BandStyleKeys.LAYOUT_ROW);
for (int i = 0; i < details.size(); i += 1)
{
final CrosstabDetail crosstabDetail = details.get(i);
String title = crosstabDetail.getTitle();
if (StringUtils.isEmpty(title))
{
title = crosstabDetail.getField();
}
cell.addElement(createLabel(title, crosstabDetail.getField(), true));
}
}
protected Element createFieldItem(final String text)
{
return createFieldItem(text, null, false);
}
protected Element createFieldItem(final String fieldName,
final Class<? extends AggregationFunction> aggregationType,
final boolean split)
{
final ElementType targetType;
if (dataSchemaModel != null)
{
final DataAttributeContext context = dataSchemaModel.getDataAttributeContext();
final DataAttributes attributes = dataSchemaModel.getDataSchema().getAttributes(fieldName);
targetType = AutoGeneratorUtility.createFieldType(attributes, context);
}
else
{
targetType = TextFieldType.INSTANCE;
}
final Element element = new Element();
element.setElementType(targetType);
element.getElementType().configureDesignTimeDefaults(element, Locale.getDefault());
if (targetType instanceof NumberFieldType)
{
element.setAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.FORMAT_STRING, "0.00;-0.00");
}
element.setAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.FIELD, fieldName);
element.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, split(split, minimumWidth));
element.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, minimumHeight);
element.getStyle().setStyleProperty(ElementStyleKeys.WIDTH, split(split, prefWidth));
element.getStyle().setStyleProperty(ElementStyleKeys.HEIGHT, prefHeight);
element.getStyle().setStyleProperty(ElementStyleKeys.MAX_WIDTH, split(split, maximumWidth));
element.getStyle().setStyleProperty(ElementStyleKeys.MAX_HEIGHT, maximumHeight);
element.setAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.AGGREGATION_TYPE, aggregationType);
element.setAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING, allowMetaDataStyling);
return element;
}
protected Element createLabel(final String text, final String labelFor)
{
return createLabel(text, labelFor, false);
}
protected Element createLabel(final String text, final String labelFor, final boolean splitArea)
{
final Element element = new Element();
element.setElementType(LabelType.INSTANCE);
element.setAttribute(AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, text);
element.getStyle().setStyleProperty(ElementStyleKeys.MIN_WIDTH, split(splitArea, minimumWidth));
element.getStyle().setStyleProperty(ElementStyleKeys.MIN_HEIGHT, minimumHeight);
element.getStyle().setStyleProperty(ElementStyleKeys.WIDTH, split(splitArea, prefWidth));
element.getStyle().setStyleProperty(ElementStyleKeys.HEIGHT, prefHeight);
element.getStyle().setStyleProperty(ElementStyleKeys.MAX_WIDTH, split(splitArea, maximumWidth));
element.getStyle().setStyleProperty(ElementStyleKeys.MAX_HEIGHT, maximumHeight);
element.setAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING, allowMetaDataStyling);
if (StringUtils.isEmpty(labelFor))
{
element.setAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES, allowMetaDataAttributes);
}
else
{
element.setAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES, true);
}
element.setAttribute(AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.LABEL_FOR, labelFor);
return element;
}
private Float split(final boolean split, final Float value)
{
if (split == false)
{
return value;
}
if (value == null)
{
return null;
}
final float f = value;
return f / Math.max(1, details.size());
}
public CrosstabBuilder clone()
{
try
{
CrosstabBuilder clone = (CrosstabBuilder) super.clone();
clone.columns = (ArrayList<CrosstabDimension>) columns.clone();
clone.rows = (ArrayList<CrosstabDimension>) rows.clone();
clone.others = (ArrayList<String>) others.clone();
clone.details = (ArrayList<CrosstabDetail>) details.clone();
return clone;
}
catch (final CloneNotSupportedException e)
{
throw new IllegalStateException(e);
}
}
public CrosstabBuilder clearDimensions()
{
CrosstabBuilder clone = clone();
clone.columns.clear();
clone.rows.clear();
clone.others.clear();
clone.details.clear();
return clone;
}
}