/*! * 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 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; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; 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; } }