/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.pentaho.di.trans.steps.excelwriter; import java.util.ArrayList; import java.util.List; import java.math.BigDecimal; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.DataFormat; import org.apache.poi.ss.util.CellReference; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.pentaho.di.core.RowSet; import org.pentaho.di.core.row.value.ValueMetaBigNumber; import org.pentaho.di.core.row.value.ValueMetaInteger; import org.pentaho.di.core.row.value.ValueMetaNumber; import org.pentaho.di.core.row.value.ValueMetaFactory; import org.pentaho.di.core.row.RowMeta; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.logging.LoggingObjectInterface; import org.pentaho.di.trans.steps.mock.StepMockHelper; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; /** * Tests for applying Format and Style from cell (from a template) when writing fields */ public class ExcelWriterStep_StyleFormatTest { private StepMockHelper<ExcelWriterStepMeta, ExcelWriterStepData> stepMockHelper; private ExcelWriterStep step; private ExcelWriterStepMeta stepMeta; private ExcelWriterStepData stepData; @Before /** * Get mock helper */ public void setUp() throws Exception { stepMockHelper = new StepMockHelper<ExcelWriterStepMeta, ExcelWriterStepData>( "Excel Writer Style Format Test", ExcelWriterStepMeta.class, ExcelWriterStepData.class ); when( stepMockHelper.logChannelInterfaceFactory.create( any(), any( LoggingObjectInterface.class ) ) ).thenReturn( stepMockHelper.logChannelInterface ); verify( stepMockHelper.logChannelInterface, never() ).logError( anyString() ); verify( stepMockHelper.logChannelInterface, never() ).logError( anyString(), any( Object[].class ) ); verify( stepMockHelper.logChannelInterface, never() ).logError( anyString(), (Throwable) anyObject() ); when( stepMockHelper.trans.isRunning() ).thenReturn( true ); } @After /** * Clean-up objects */ public void tearDown() { stepData.file = null; stepData.sheet = null; stepData.wb = null; stepData.clearStyleCache( 0 ); stepMockHelper.cleanUp(); } @Test /** * Test applying Format and Style from cell for XLS file format */ public void testStyleFormatHssf() throws Exception { testStyleFormat( "xls" ); } @Test /** * Test applying Format and Style from cell for XLSX file format */ public void testStyleFormatXssf() throws Exception { testStyleFormat( "xlsx" ); } /** * Test applying Format and Style from cell (from a template) when writing fields * * @param fileType * @throws Exception */ private void testStyleFormat( String fileType ) throws Exception { setupStepMock( fileType ); createStepMeta( fileType ); createStepData( fileType ); step.init( stepMeta, stepData ); // We do not run transformation or executing the whole step // instead we just execute ExcelWriterStepData.writeNextLine() to write to Excel workbook object // Values are written in A2:D2 and A3:D3 rows List<Object[]> rows = createRowData(); for ( int i = 0; i < rows.size(); i++ ) { step.writeNextLine( rows.get( i ) ); } // Custom styles are loaded from G1 cell Row xlsRow = stepData.sheet.getRow( 0 ); Cell baseCell = xlsRow.getCell( 6 ); CellStyle baseCellStyle = baseCell.getCellStyle(); DataFormat format = stepData.wb.createDataFormat(); // Check style of the exported values in A3:D3 xlsRow = stepData.sheet.getRow( 2 ); for ( int i = 0; i < stepData.inputRowMeta.size(); i++ ) { Cell cell = xlsRow.getCell( i ); CellStyle cellStyle = cell.getCellStyle(); if ( i > 0 ) { assertEquals( cellStyle.getBorderRight(), baseCellStyle.getBorderRight() ); assertEquals( cellStyle.getFillPattern(), baseCellStyle.getFillPattern() ); } else { // cell A2/A3 has no custom style assertFalse( cellStyle.getBorderRight() == baseCellStyle.getBorderRight() ); assertFalse( cellStyle.getFillPattern() == baseCellStyle.getFillPattern() ); } if ( i != 1 ) { assertEquals( format.getFormat( cellStyle.getDataFormat() ), "0.00000" ); } else { // cell B2/B3 use different format from the custom style assertEquals( format.getFormat( cellStyle.getDataFormat() ), "##0,000.0" ); } } } /** * Setup any meta information for Excel Writer step * * @param fileType * @throws KettleException */ private void createStepMeta( String fileType ) throws KettleException { stepMeta = new ExcelWriterStepMeta(); stepMeta.setDefault(); stepMeta.setFileName( "testExcel" ); stepMeta.setExtension( fileType ); stepMeta.setSheetname( "Sheet1" ); stepMeta.setHeaderEnabled( true ); stepMeta.setStartingCell( "A2" ); // Try different combinations of specifying data format and style from cell // 1. Only format, no style // 2. No format, only style // 3. Format, and a different style without a format defined // 4. Format, and a different style with a different format defined but gets overridden ExcelWriterStepField[] outputFields = new ExcelWriterStepField[4]; outputFields[0] = new ExcelWriterStepField( "col 1", ValueMetaFactory.getIdForValueMeta( "Integer" ), "0.00000" ); outputFields[0].setStyleCell( "" ); outputFields[1] = new ExcelWriterStepField( "col 2", ValueMetaFactory.getIdForValueMeta( "Number" ), "" ); outputFields[1].setStyleCell( "G1" ); outputFields[2] = new ExcelWriterStepField( "col 3", ValueMetaFactory.getIdForValueMeta( "BigNumber" ), "0.00000" ); outputFields[2].setStyleCell( "F1" ); outputFields[3] = new ExcelWriterStepField( "col 4", ValueMetaFactory.getIdForValueMeta( "Integer" ), "0.00000" ); outputFields[3].setStyleCell( "G1" ); stepMeta.setOutputFields( outputFields ); } /** * Setup the data necessary for Excel Writer step * * @param fileType * @throws KettleException */ private void createStepData( String fileType ) throws KettleException { stepData = new ExcelWriterStepData(); stepData.inputRowMeta = step.getInputRowMeta().clone(); stepData.outputRowMeta = step.getInputRowMeta().clone(); // we don't run transformation so ExcelWriterStep.processRow() doesn't get executed // we populate the ExcelWriterStepData with bare minimum required values CellReference cellRef = new CellReference( stepMeta.getStartingCell() ); stepData.startingRow = cellRef.getRow(); stepData.startingCol = cellRef.getCol(); stepData.posX = stepData.startingCol; stepData.posY = stepData.startingRow; int numOfFields = stepData.inputRowMeta.size(); stepData.fieldnrs = new int[numOfFields]; stepData.linkfieldnrs = new int[numOfFields]; stepData.commentfieldnrs = new int[numOfFields]; for ( int i = 0; i < numOfFields; i++ ) { stepData.fieldnrs[i] = i; stepData.linkfieldnrs[i] = -1; stepData.commentfieldnrs[i] = -1; } // we avoid reading/writing Excel files, so ExcelWriterStep.prepareNextOutputFile() doesn't get executed // create Excel workbook object stepData.wb = stepMeta.getExtension().equalsIgnoreCase( "xlsx" ) ? new XSSFWorkbook() : new HSSFWorkbook(); stepData.sheet = stepData.wb.createSheet(); stepData.file = null; stepData.clearStyleCache( numOfFields ); // we avoid reading template file from disk // so set beforehand cells with custom style and formatting DataFormat format = stepData.wb.createDataFormat(); Row xlsRow = stepData.sheet.createRow( 0 ); // Cell F1 has custom style applied, used as template Cell cell = xlsRow.createCell( 5 ); CellStyle cellStyle = stepData.wb.createCellStyle(); cellStyle.setBorderRight( CellStyle.BORDER_THICK ); cellStyle.setFillPattern( CellStyle.FINE_DOTS ); cell.setCellStyle( cellStyle ); // Cell G1 has same style, but also a custom data format cellStyle = stepData.wb.createCellStyle(); cellStyle.cloneStyleFrom( cell.getCellStyle() ); cell = xlsRow.createCell( 6 ); cellStyle.setDataFormat( format.getFormat( "##0,000.0" ) ); cell.setCellStyle( cellStyle ); } /** * Create ExcelWriterStep object and mock some of its required data * * @param fileType * @throws Exception */ private void setupStepMock( String fileType ) throws Exception { step = new ExcelWriterStep( stepMockHelper.stepMeta, stepMockHelper.stepDataInterface, 0, stepMockHelper.transMeta, stepMockHelper.trans ); step.init( stepMockHelper.initStepMetaInterface, stepMockHelper.initStepDataInterface ); List<Object[]> rows = createRowData(); String[] outFields = new String[] { "col 1", "col 2", "col 3", "col 4" }; RowSet inputRowSet = stepMockHelper.getMockInputRowSet( rows ); RowMetaInterface inputRowMeta = createRowMeta(); inputRowSet.setRowMeta( inputRowMeta ); RowMetaInterface mockOutputRowMeta = mock( RowMetaInterface.class ); when( mockOutputRowMeta.size() ).thenReturn( outFields.length ); when( inputRowSet.getRowMeta() ).thenReturn( inputRowMeta ); step.getInputRowSets().add( inputRowSet ); step.setInputRowMeta( inputRowMeta ); step.getOutputRowSets().add( inputRowSet ); } /** * Create data rows that are passed to Excel Writer step * * @return * @throws Exception */ private ArrayList<Object[]> createRowData() throws Exception { ArrayList<Object[]> rows = new ArrayList<Object[]>(); Object[] row = new Object[] { new Long( 123456 ), new Double( 2.34e-4 ), new BigDecimal( "123456789.987654321" ), new Double( 504150 ) }; rows.add( row ); row = new Object[] { new Long( 1001001 ), new Double( 4.6789e10 ), new BigDecimal( 123123e-2 ), new Double( 12312300 ) }; rows.add( row ); return rows; } /** * Create meta information for rows that are passed to Excel Writer step * * @return * @throws KettleException */ private RowMetaInterface createRowMeta() throws KettleException { RowMetaInterface rm = new RowMeta(); try { ValueMetaInterface[] valuesMeta = { new ValueMetaInteger( "col 1" ), new ValueMetaNumber( "col 2" ), new ValueMetaBigNumber( "col 3" ), new ValueMetaNumber( "col 4" ) }; for ( int i = 0; i < valuesMeta.length; i++ ) { rm.addValueMeta( valuesMeta[i] ); } } catch ( Exception ex ) { return null; } return rm; } }