/*
* 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) 2006 - 2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Before;
import org.junit.Test;
import org.pentaho.reporting.engine.classic.core.filter.types.RotatableText;
import org.pentaho.reporting.engine.classic.core.function.ExpressionRuntime;
import org.pentaho.reporting.engine.classic.core.function.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.modules.output.pageable.plaintext.PlainTextReportUtil;
import org.pentaho.reporting.engine.classic.core.modules.output.table.csv.CSVReportUtil;
import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlReportUtil;
import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlTableModule;
import org.pentaho.reporting.engine.classic.core.modules.output.table.xls.ExcelReportUtil;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleSheet;
import org.pentaho.reporting.engine.classic.core.style.TextRotation;
import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys;
import org.pentaho.reporting.engine.classic.core.util.RotatedTextDrawable;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class RotationTest {
@Before
public void setUp() throws IOException {
ClassicEngineBoot.getInstance().start();
}
@Test
public void testXLS() throws ResourceException, IOException {
URL url = getClass().getResource( "BACKLOG-6818.prpt" );
final File testOutputFile = File.createTempFile( "test", ".xls" );
MasterReport report = (MasterReport) new ResourceManager().createDirectly( url, MasterReport.class ).getResource();
try ( FileOutputStream stream = new FileOutputStream( testOutputFile ) ) {
ExcelReportUtil.createXLS( report, stream );
HSSFWorkbook workbook = new HSSFWorkbook( new FileInputStream( testOutputFile ) );
assertNotNull( workbook );
final HSSFSheet sheet = workbook.getSheetAt( 0 );
assertNotNull( sheet );
final Iterator<Row> rowIterator = sheet.rowIterator();
assertNotNull( rowIterator );
final Row next = rowIterator.next();
assertNotNull( next );
int k = 1;
for ( int i = 0; i < 5; i++, k = -k ) {
final Cell cell = next.getCell( i );
assertNotNull( cell );
assertTrue( cell.getCellStyle().getRotation() == k * 90 );
}
for ( int i = 6; i < 9; i++, k = -k ) {
final Cell cell = next.getCell( i );
assertNull( cell );
}
} catch ( IOException | ReportProcessingException e ) {
fail();
} finally {
assertTrue( testOutputFile.delete() );
}
}
@Test
public void testXLSX() throws ResourceException, IOException {
URL url = getClass().getResource( "BACKLOG-6818.prpt" );
final File testOutputFile = File.createTempFile( "test", ".xlsx" );
MasterReport report = (MasterReport) new ResourceManager().createDirectly( url, MasterReport.class ).getResource();
try ( FileOutputStream stream = new FileOutputStream( testOutputFile ) ) {
ExcelReportUtil.createXLSX( report, stream );
XSSFWorkbook workbook = new XSSFWorkbook( new FileInputStream( testOutputFile ) );
assertNotNull( workbook );
final XSSFSheet sheet = workbook.getSheetAt( 0 );
assertNotNull( sheet );
final Iterator<Row> rowIterator = sheet.rowIterator();
assertNotNull( rowIterator );
final Row next = rowIterator.next();
assertNotNull( next );
int k = 1;
for ( int i = 0; i < 5; i++, k = -k ) {
final Cell cell = next.getCell( i );
assertNotNull( cell );
assertTrue( cell.getCellStyle().getRotation() == ( k > 0 ? 90 : 180 ) );
}
for ( int i = 6; i < 9; i++, k = -k ) {
final Cell cell = next.getCell( i );
assertNull( cell );
}
} catch ( IOException | ReportProcessingException e ) {
fail();
} finally {
assertTrue( testOutputFile.delete() );
}
}
@Test
public void testHTML() throws ResourceException, IOException {
URL url = getClass().getResource( "BACKLOG-6818.prpt" );
MasterReport report = (MasterReport) new ResourceManager().createDirectly( url, MasterReport.class ).getResource();
try ( ByteArrayOutputStream stream = new ByteArrayOutputStream() ) {
HtmlReportUtil.createStreamHTML( report, stream );
final String html = new String( stream.toByteArray(), "UTF-8" );
int k = 1;
for ( int i = 1; i < 5; i++, k = -k ) {
final Pattern pattern = Pattern.compile( "(.*id=\"test" + i + "\".*style=\")(.*)(\".*)" );
final Matcher matcher = pattern.matcher( html );
if ( matcher.find() ) {
final String group = matcher.group( 2 );
assertTrue( group.contains( k > 0 ? TextRotation.D_90.getCss() : TextRotation.D_270.getCss() ) );
}
}
} catch ( final IOException | ReportProcessingException e ) {
fail();
}
}
@Test
public void testGetInstance() {
assertEquals( TextRotation.getInstance( (short) 90 ), TextRotation.D_90 );
assertEquals( TextRotation.getInstance( (short) -90 ), TextRotation.D_270 );
}
@Test
public void testCss() {
assertEquals( TextRotation.D_90.getCss(),
"transform: rotate(-90deg); -ms-transform: rotate(-90deg); -webkit-transform: rotate(-90deg); white-space: "
+ "nowrap; transform-origin: right bottom;" );
assertEquals( TextRotation.D_270.getCss(),
"transform: rotate(90deg); -ms-transform: rotate(90deg); -webkit-transform: rotate(90deg); white-space: nowrap;"
+ " transform-origin: left bottom;" );
}
@Test
public void testTxt() throws ResourceException {
URL url = getClass().getResource( "BACKLOG-6818.prpt" );
MasterReport report = (MasterReport) new ResourceManager().createDirectly( url, MasterReport.class ).getResource();
try ( ByteArrayOutputStream stream = new ByteArrayOutputStream() ) {
PlainTextReportUtil.createPlainText( report, stream );
final byte[] bytes = stream.toByteArray();
assertNotNull( bytes );
assertTrue( bytes.length > 0 );
assertTrue( StringUtils.isNotBlank( new String( bytes, "UTF-8" ) ) );
} catch ( final IOException | ReportProcessingException e ) {
fail();
}
}
@Test
public void testCSV() throws ResourceException {
URL url = getClass().getResource( "BACKLOG-6818.prpt" );
MasterReport report = (MasterReport) new ResourceManager().createDirectly( url, MasterReport.class ).getResource();
try ( ByteArrayOutputStream stream = new ByteArrayOutputStream() ) {
CSVReportUtil.createCSV( report, stream, "UTF-8" );
final byte[] bytes = stream.toByteArray();
assertNotNull( bytes );
assertTrue( bytes.length > 0 );
assertTrue( StringUtils.isNotBlank( new String( bytes, "UTF-8" ).replaceAll( ",", "" ) ) );
} catch ( final IOException | ReportProcessingException e ) {
fail();
}
}
@Test
public void testRotationSupport() {
assertFalse( RotatableText.isRotationSupported( null ) );
final ExpressionRuntime runtime = mock( ExpressionRuntime.class );
assertFalse( RotatableText.isRotationSupported( runtime ) );
final ProcessingContext processingContext = mock( ProcessingContext.class );
when( runtime.getProcessingContext() ).thenReturn( processingContext );
assertFalse( RotatableText.isRotationSupported( runtime ) );
final OutputProcessorMetaData metaData = mock( OutputProcessorMetaData.class );
when( processingContext.getOutputProcessorMetaData() ).thenReturn( metaData );
when( metaData.isFeatureSupported( eq( OutputProcessorFeature.IGNORE_ROTATION ) ) ).thenReturn( true );
assertFalse( RotatableText.isRotationSupported( runtime ) );
when( metaData.isFeatureSupported( eq( OutputProcessorFeature.IGNORE_ROTATION ) ) ).thenReturn( false );
assertTrue( RotatableText.isRotationSupported( runtime ) );
RotatableText rt = new RotatableText() {
};
assertNull( rt.rotate( null, null, null ) );
final UUID uuid = UUID.randomUUID();
assertEquals( uuid, rt.rotate( null, uuid, null ) );
final ReportElement reportElement = mock( ReportElement.class );
assertEquals( uuid, rt.rotate( reportElement, uuid, null ) );
assertEquals( uuid, rt.rotate( reportElement, uuid, runtime ) );
final ElementStyleSheet sheet = mock( ElementStyleSheet.class );
when( reportElement.getStyle() ).thenReturn( sheet );
assertEquals( uuid, rt.rotate( reportElement, uuid, runtime ) );
when( sheet.getStyleProperty( eq( TextStyleKeys.TEXT_ROTATION ), isNull() ) ).thenReturn( "blabla" );
assertEquals( uuid, rt.rotate( reportElement, uuid, runtime ) );
when( sheet.getStyleProperty( eq( TextStyleKeys.TEXT_ROTATION ), isNull() ) ).thenReturn( TextRotation.D_90 );
assertTrue( rt.rotate( reportElement, uuid, runtime ) instanceof RotatedTextDrawable );
}
@Test
public void testHandleRotatedTextHTML() throws Exception {
URL url = getClass().getResource( "BACKLOG-10064.prpt" );
MasterReport report = (MasterReport) new ResourceManager().createDirectly( url, MasterReport.class ).getResource();
report.getReportConfiguration().setConfigProperty( HtmlTableModule.INLINE_STYLE, "true" );
List<String> elementsIdList = Arrays.asList("topLeft90","topCenter90","topRight90","topJustify90",
"topLeft-90","topCenter-90","topRight-90","topJustify-90",
"middleLeft90","middleCenter90","middleRight90","middleJustify90",
"middleLeft-90","middleCenter-90","middleRight-90","middleJustify-90",
"bottomLeft90","bottomCenter90","bottomRight90","bottomJustify90",
"bottomLeft-90","bottomCenter-90","bottomRight-90","bottomJustify-90");
XPathFactory xpathFactory = XPathFactory.newInstance();
try ( ByteArrayOutputStream stream = new ByteArrayOutputStream() ) {
HtmlReportUtil.createStreamHTML( report, stream );
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware( true );
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse( new ByteArrayInputStream( stream.toByteArray() ) );
for ( String elementId : elementsIdList ) {
org.w3c.dom.Element element = document.getElementById( elementId );
Node rotatedTextStyle = element.getFirstChild().getAttributes().getNamedItem( "style" );
Node trSryle = element.getParentNode().getParentNode().getAttributes().getNamedItem( "style" );
assertTrue( isStyleValid( rotatedTextStyle.getNodeValue(), trSryle.getNodeValue(), elementId.contains( "middle" ) ) );
}
} catch ( final IOException | ReportProcessingException e ) {
fail();
}
}
private boolean isStyleValid( String rotatedTextStyle, String trSryle, boolean isMiddleAlign ) {
String rotatedTextHeight;
if ( isMiddleAlign ) {
rotatedTextHeight = rotatedTextStyle.substring( rotatedTextStyle.indexOf( "height: " ) + 8, rotatedTextStyle.indexOf( "pt" ) );
} else {
rotatedTextHeight = rotatedTextStyle.substring( rotatedTextStyle.indexOf( "width: " ) + 7, rotatedTextStyle.indexOf( "pt" ) );
}
String trHeight = trSryle.substring( trSryle.indexOf( "height: " ) + 8, trSryle.indexOf( "pt" ) );
return rotatedTextHeight.equals( trHeight );
}
}