/*!
* 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.function;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.SimplePageDefinition;
import org.pentaho.reporting.engine.classic.core.TableDataFactory;
import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeDataSchemaModel;
import org.pentaho.reporting.engine.classic.core.filter.types.TextFieldType;
import org.pentaho.reporting.engine.classic.core.filter.types.bands.GroupFooterType;
import org.pentaho.reporting.engine.classic.core.filter.types.bands.GroupHeaderType;
import org.pentaho.reporting.engine.classic.core.filter.types.bands.ItemBandType;
import org.pentaho.reporting.engine.classic.core.layout.ModelPrinter;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.testsupport.DebugReportRunner;
import org.pentaho.reporting.engine.classic.core.testsupport.RelationalReportBuilder;
import org.pentaho.reporting.engine.classic.core.testsupport.selector.AndMatcher;
import org.pentaho.reporting.engine.classic.core.testsupport.selector.AttributeMatcher;
import org.pentaho.reporting.engine.classic.core.testsupport.selector.ElementTypeMatcher;
import org.pentaho.reporting.engine.classic.core.testsupport.selector.MatchFactory;
import org.pentaho.reporting.engine.classic.core.util.PageSize;
import org.pentaho.reporting.engine.classic.core.util.TypedTableModel;
import org.pentaho.reporting.libraries.base.util.DebugLog;
import javax.swing.table.TableModel;
import java.awt.*;
import java.util.List;
/**
* Tests the TotalPage* functions: <code>TotalPageSumFunction</code> and <code>TotalPageItemCountFunction</code>
*
* @noinspection HardCodedStringLiteral
*/
public class TotalPageFunctionsIT extends TestCase {
public static final String ROW_DIMENSION_A = "Row-Dimension-A";
public static final String ROW_DIMENSION_B = "Row-Dimension-B";
public static final String COLUMN_DIMENSION_A = "Column-Dimension-A";
public static final String COLUMN_DIMENSION_B = "Column-Dimension-B";
public static final String VALUE = "Value";
public static final Color VALUE_BACKGROUND = new Color( 178, 178, 255 );
public static final Color ROWA_BACKGROUND = new Color( 255, 178, 255 );
public static final Color ROWA_VALIDATE_BACKGROUND = new Color( 255, 208, 255 );
public static final Color ROWB_BACKGROUND = new Color( 178, 255, 255 );
public static final Color ROWB_VALIDATE_BACKGROUND = new Color( 208, 255, 255 );
public static final Color ROWC_BACKGROUND = new Color( 178, 178, 208 );
public static final Color ROWC_VALIDATE_BACKGROUND = new Color( 178, 208, 178 );
public TotalPageFunctionsIT() {
}
protected void setUp() throws Exception {
ClassicEngineBoot.getInstance().start();
}
private TableModel createRelationalTableModel() {
final TypedTableModel model = new TypedTableModel();
model.addColumn( ROW_DIMENSION_A, String.class );
model.addColumn( ROW_DIMENSION_B, String.class );
model.addColumn( VALUE, String.class );
model.addColumn( "validate-row-b-sum", Integer.class );
model.addColumn( "validate-row-a-sum", Integer.class );
model.addColumn( "validate-no-group", Integer.class );
model.addRow( "RA", "r1", 1, 1, 5, 5 );
model.addRow( "RA", "r2", 1, 1, 5, 5 );
model.addRow( "RA", "r1", 1, 3, 5, 5 );
model.addRow( "RA", "r1", 1, 3, 5, 5 );
model.addRow( "RA", "r1", 1, 3, 5, 5 ); // page break
model.addRow( "RA", "r2", 1, 3, 3, 7 );
model.addRow( "RA", "r2", 1, 3, 3, 7 );
model.addRow( "RA", "r2", 1, 3, 3, 7 );
model.addRow( "RB", "r1", 1, 4, 4, 7 );
model.addRow( "RB", "r1", 1, 4, 4, 7 );
model.addRow( "RB", "r1", 1, 4, 4, 7 );
model.addRow( "RB", "r1", 1, 4, 4, 7 ); // page break
model.addRow( "RB", "r1", 1, 1, 8, 8 );
model.addRow( "RB", "r2", 1, 7, 8, 8 );
model.addRow( "RB", "r2", 1, 7, 8, 8 );
model.addRow( "RB", "r2", 1, 7, 8, 8 );
model.addRow( "RB", "r2", 1, 7, 8, 8 );
model.addRow( "RB", "r2", 1, 7, 8, 8 );
model.addRow( "RB", "r2", 1, 7, 8, 8 );
model.addRow( "RB", "r2", 1, 7, 8, 8 );
return model;
}
private AggregationFunction create( final String name, final String group, final Class aggFunction ) {
AggregationFunction detailsSum = null;
if ( aggFunction.equals( TotalPageItemCountFunction.class ) ) {
detailsSum = new TotalPageItemCountFunction();
} else if ( aggFunction.equals( TotalPageSumFunction.class ) ) {
detailsSum = new TotalPageSumFunction();
( (TotalPageSumFunction) detailsSum ).setField( VALUE );
}
detailsSum.setName( name );
detailsSum.setGroup( group );
detailsSum.setDependencyLevel( 1 );
return detailsSum;
}
public void testTotalPageItemCountRelational() throws Exception {
// http://jira.pentaho.com/browse/PRD-4216
validateRelationalReport( TotalPageItemCountFunction.class );
}
/**
* The VALUE field is defined with a '1' for each row in the model, so it's possible to use the same expected values
* for both the PageItemCount and PageSum functions.
*
* @throws Exception
*/
public void testTotalPageSumRelational() throws Exception {
// http://jira.pentaho.com/browse/PRD-4217
validateRelationalReport( TotalPageSumFunction.class );
}
private void validateRelationalReport( final Class aggFun ) throws Exception {
final TableModel tableModel = createRelationalTableModel();
final MasterReport report = createRelationalReport( tableModel, aggFun );
// Null means the result is undefined.
final Integer[][] rowAHeaderValues = { { 5, 5, 5, 5, 5 }, // page 0
{ 3, 3, 4, 4 }, // page 1
{ 8, 8, 8 } // page 2
};
final Integer[][] rowBHeaderValues = { { null, 1, 1, 3, 0 }, // page 0
{ null, 3, null, 4 }, // page 1
{ 1, 1, 7 } // page 2
};
final Integer[][] noGrpHeaderValues = { { 5, 5, 5, 5, 5 }, // page 0
{ 7, 7, 7, 7 }, // page 1
{ 8, 8, 8 } // page 2
};
final Integer[][] rowAFooterValues = { { 5, 5, 5, 5, 5 }, // page 0
{ 3, 3, 4, 4 }, // page 1
{ 8, 8, 8 } // page 2
};
final Integer[][] rowBFooterValues = { { 1, 1, 3, 0, 0 }, // page 0
{ 3, null, 4, null }, // page 1
{ 1, 7, null } // page 2
};
final Integer[][] noGrpFooterValues = { { 5, 5, 5, 5, 5 }, // page 0
{ 7, 7, 7, 7 }, // page 1
{ 8, 8, 8 } // page 2
};
report.addExpression( create( "row-b-sum", "::group-1", aggFun ) );
report.addExpression( new ValidatePageFunctionResultExpression( "#row-b-sum", null ) );
report.addExpression( create( "row-a-sum", "::group-0", aggFun ) );
report.addExpression( new ValidatePageFunctionResultExpression( "#row-a-sum", null ) );
report.addExpression( create( "no-group", null, aggFun ) );
report.addExpression( new ValidatePageFunctionResultExpression( "#no-group", null ) );
DebugReportRunner.showDialog( report );
List<LogicalPageBox> logicalPageBoxes = DebugReportRunner.layoutPages( report, 0, 1, 2 );
for ( int page = 0; page < 3; page += 1 ) {
final LogicalPageBox logicalPageBox = logicalPageBoxes.get( page );
validateItemBands( logicalPageBox );
validateHeader( rowAHeaderValues[page], rowBHeaderValues[page], noGrpHeaderValues[page], page, logicalPageBox );
validateFooter( rowAFooterValues[page], rowBFooterValues[page], noGrpHeaderValues[page], page, logicalPageBox );
}
// DebugReportRunner.execGraphics2D(report);
}
/**
* Compares the header by their order of occurrence on the page. Each footer has text-fields which carry their
* currently calculated value in their attributes. (This is done by a CopyValueAsTextExpression, which is set up by
* the RelationalReportBuilder.)
* <p/>
* Each test maintains a human-provided list of expected values per page.
*
* @param rowAHeaderValue
* @param rowBHeaderValue
* @param noGrpValue
* @param page
* @param logicalPageBox
*/
private void validateHeader( final Integer[] rowAHeaderValue, final Integer[] rowBHeaderValue,
final Integer[] noGrpValue, final int page, final LogicalPageBox logicalPageBox ) {
final RenderNode[] groupHeaders = MatchFactory.findElementsByElementType( logicalPageBox, GroupHeaderType.INSTANCE );
for ( int i = 0; i < groupHeaders.length; i++ ) {
final RenderNode groupHeader = groupHeaders[i];
final RenderNode rowASum = findTextFieldsWithField( "row-a-sum", groupHeader );
final RenderNode rowBSum = findTextFieldsWithField( "row-b-sum", groupHeader );
final RenderNode noGrp = findTextFieldsWithField( "no-group", groupHeader );
final Integer expectedA = rowAHeaderValue[i];
final Object valueA = rowASum.getAttributes().getAttribute( "test-run", "test-value" );
final Integer expectedB = rowBHeaderValue[i];
final Object valueB = rowBSum.getAttributes().getAttribute( "test-run", "test-value" );
final Integer expectedC = noGrpValue[i];
final Object valueC = noGrp.getAttributes().getAttribute( "test-run", "test-value" );
try {
if ( expectedA != null ) {
assertEquals( "Row-A", String.valueOf( expectedA ), valueA );
}
if ( expectedB != null ) {
assertEquals( "Row-B", String.valueOf( expectedB ), valueB );
}
if ( expectedC != null ) {
assertEquals( "No group", String.valueOf( expectedC ), valueC );
}
} catch ( AssertionFailedError afe ) {
DebugLog.log( "Failed on page " + page );
ModelPrinter.INSTANCE.print( groupHeader );
throw afe;
}
}
}
/**
* Compares the footer by their order of occurrence on the page. Each footer has text-fields which carry their
* currently calculated value in their attributes. (This is done by a CopyValueAsTextExpression, which is set up by
* the RelationalReportBuilder.)
* <p/>
* Each test maintains a human-provided list of expected values per page.
*
* @param rowAHeaderValue
* @param rowBHeaderValue
* @param noGrpValue
* @param page
* @param logicalPageBox
*/
private void validateFooter( final Integer[] rowAHeaderValue, final Integer[] rowBHeaderValue,
final Integer[] noGrpValue, final int page, final LogicalPageBox logicalPageBox ) {
final RenderNode[] groupFooters = MatchFactory.findElementsByElementType( logicalPageBox, GroupFooterType.INSTANCE );
for ( int i = 0; i < groupFooters.length; i++ ) {
final RenderNode groupFooter = groupFooters[i];
final RenderNode rowASum = findTextFieldsWithField( "row-a-sum", groupFooter );
final RenderNode rowBSum = findTextFieldsWithField( "row-b-sum", groupFooter );
final RenderNode noGrp = findTextFieldsWithField( "no-group", groupFooter );
final Integer expectedA = rowAHeaderValue[i];
final Object valueA = rowASum.getAttributes().getAttribute( "test-run", "test-value" );
final Integer expectedB = rowBHeaderValue[i];
final Object valueB = rowBSum.getAttributes().getAttribute( "test-run", "test-value" );
final Integer expectedC = noGrpValue[i];
final Object valueC = noGrp.getAttributes().getAttribute( "test-run", "test-value" );
try {
if ( expectedA != null ) {
assertEquals( "Row-A", String.valueOf( expectedA ), valueA );
}
if ( expectedB != null ) {
assertEquals( "Row-B", String.valueOf( expectedB ), valueB );
}
if ( expectedC != null ) {
assertEquals( "No group", String.valueOf( expectedC ), valueC );
}
} catch ( AssertionFailedError afe ) {
DebugLog.log( "Failed on page " + page );
ModelPrinter.INSTANCE.print( groupFooter );
throw afe;
}
}
}
/**
* Compares the expression result directly with the result of the validation expression as found in the detail band.
* For all Total*Page functions, this should always match.
*
* @param logicalPageBox
*/
private void validateItemBands( final LogicalPageBox logicalPageBox ) {
final RenderNode[] itemBands = MatchFactory.findElementsByElementType( logicalPageBox, ItemBandType.INSTANCE );
for ( int i = 0; i < itemBands.length; i++ ) {
final RenderNode itemBand = itemBands[i];
final RenderNode rowASum = findTextFieldsWithField( "row-a-sum", itemBand );
final RenderNode rowBSum = findTextFieldsWithField( "row-b-sum", itemBand );
final RenderNode noGrp = findTextFieldsWithField( "no-group", itemBand );
final RenderNode rowAValidate = findTextFieldsWithField( "#row-a-sum", itemBand );
final RenderNode rowBValidate = findTextFieldsWithField( "#row-b-sum", itemBand );
final RenderNode noGrpValidate = findTextFieldsWithField( "#no-group", itemBand );
assertEquals( rowAValidate.getAttributes().getAttribute( "test-run", "test-value" ), rowASum.getAttributes()
.getAttribute( "test-run", "test-value" ) );
assertEquals( rowBValidate.getAttributes().getAttribute( "test-run", "test-value" ), rowBSum.getAttributes()
.getAttribute( "test-run", "test-value" ) );
assertEquals( noGrpValidate.getAttributes().getAttribute( "test-run", "test-value" ), noGrp.getAttributes()
.getAttribute( "test-run", "test-value" ) );
}
}
private RenderNode findTextFieldsWithField( String field, RenderNode itemBand ) {
final ElementTypeMatcher typeMatcher = new ElementTypeMatcher( TextFieldType.INSTANCE.getMetaData().getName() );
final AttributeMatcher attributeMatcher =
new AttributeMatcher( AttributeNames.Core.NAMESPACE, AttributeNames.Core.FIELD, field );
final AndMatcher m = new AndMatcher( typeMatcher, attributeMatcher );
return MatchFactory.match( itemBand, m );
}
private MasterReport createRelationalReport( final TableModel tableModel, final Class aggFun ) {
final MasterReport report = new MasterReport();
report.setPageDefinition( new SimplePageDefinition( new PageSize( 800, 300 ) ) );
report.setDataFactory( new TableDataFactory( "query", tableModel ) );
report.setQuery( "query" );
final DesignTimeDataSchemaModel dataSchemaModel = new DesignTimeDataSchemaModel( report );
final RelationalReportBuilder builder = new RelationalReportBuilder( dataSchemaModel );
builder.addGroup( ROW_DIMENSION_A );
builder.addGroup( ROW_DIMENSION_B );
builder.addDetails( VALUE, aggFun, VALUE_BACKGROUND );
builder.addDetails( "row-a-sum", null, ROWA_BACKGROUND );
builder.addDetails( "#row-a-sum", null, ROWA_VALIDATE_BACKGROUND );
builder.addDetails( "row-b-sum", null, ROWB_BACKGROUND );
builder.addDetails( "#row-b-sum", null, ROWB_VALIDATE_BACKGROUND );
builder.addDetails( "no-group", null, ROWC_BACKGROUND );
builder.addDetails( "#no-group", null, ROWC_VALIDATE_BACKGROUND );
report.setRootGroup( builder.create() );
return report;
}
}